From 5f2c5c69a61dc5411d436c1a422f8a1ee195a924 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Jul 2022 12:21:07 +0200 Subject: media: v4l2-ctrls: allocate space for arrays Just like dynamic arrays, also allocate space for regular arrays. This is in preparation for allowing to change the array size from a driver. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-api.c | 8 ++++---- drivers/media/v4l2-core/v4l2-ctrls-core.c | 33 +++++++++++++++---------------- include/media/v4l2-ctrls.h | 17 ++++++++-------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 50d012ba3c02..1b90bd7c4010 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -105,8 +105,8 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) ctrl->is_new = 0; if (ctrl->is_dyn_array && - c->size > ctrl->p_dyn_alloc_elems * ctrl->elem_size) { - void *old = ctrl->p_dyn; + c->size > ctrl->p_array_alloc_elems * ctrl->elem_size) { + void *old = ctrl->p_array; void *tmp = kvzalloc(2 * c->size, GFP_KERNEL); if (!tmp) @@ -115,8 +115,8 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) memcpy(tmp + c->size, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); ctrl->p_new.p = tmp; ctrl->p_cur.p = tmp + c->size; - ctrl->p_dyn = tmp; - ctrl->p_dyn_alloc_elems = c->size / ctrl->elem_size; + ctrl->p_array = tmp; + ctrl->p_array_alloc_elems = c->size / ctrl->elem_size; kvfree(old); } diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 1f85828d6694..9871c77f559b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -1135,14 +1135,14 @@ int req_to_new(struct v4l2_ctrl_ref *ref) /* * Check if the number of elements in the request is more than the - * elements in ctrl->p_dyn. If so, attempt to realloc ctrl->p_dyn. - * Note that p_dyn is allocated with twice the number of elements + * elements in ctrl->p_array. If so, attempt to realloc ctrl->p_array. + * Note that p_array is allocated with twice the number of elements * in the dynamic array since it has to store both the current and * new value of such a control. */ - if (ref->p_req_elems > ctrl->p_dyn_alloc_elems) { + if (ref->p_req_elems > ctrl->p_array_alloc_elems) { unsigned int sz = ref->p_req_elems * ctrl->elem_size; - void *old = ctrl->p_dyn; + void *old = ctrl->p_array; void *tmp = kvzalloc(2 * sz, GFP_KERNEL); if (!tmp) @@ -1151,8 +1151,8 @@ int req_to_new(struct v4l2_ctrl_ref *ref) memcpy(tmp + sz, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); ctrl->p_new.p = tmp; ctrl->p_cur.p = tmp + sz; - ctrl->p_dyn = tmp; - ctrl->p_dyn_alloc_elems = ref->p_req_elems; + ctrl->p_array = tmp; + ctrl->p_array_alloc_elems = ref->p_req_elems; kvfree(old); } @@ -1252,7 +1252,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) list_del(&ctrl->node); list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node) list_del(&sev->node); - kvfree(ctrl->p_dyn); + kvfree(ctrl->p_array); kvfree(ctrl); } kvfree(hdl->buckets); @@ -1584,11 +1584,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) && + else if (!is_array && (type == V4L2_CTRL_TYPE_INTEGER64 || type == V4L2_CTRL_TYPE_STRING || - type >= V4L2_CTRL_COMPOUND_TYPES || - is_array)) + type >= V4L2_CTRL_COMPOUND_TYPES)) sz_extra += 2 * tot_ctrl_size; if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) @@ -1632,14 +1631,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->cur.val = ctrl->val = def; data = &ctrl[1]; - if (ctrl->is_dyn_array) { - ctrl->p_dyn_alloc_elems = elems; - ctrl->p_dyn = kvzalloc(2 * elems * elem_size, GFP_KERNEL); - if (!ctrl->p_dyn) { + if (ctrl->is_array) { + ctrl->p_array_alloc_elems = elems; + ctrl->p_array = kvzalloc(2 * elems * elem_size, GFP_KERNEL); + if (!ctrl->p_array) { kvfree(ctrl); return NULL; } - data = ctrl->p_dyn; + data = ctrl->p_array; } if (!ctrl->is_int) { @@ -1651,7 +1650,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) { - if (ctrl->is_dyn_array) + if (ctrl->is_array) ctrl->p_def.p = &ctrl[1]; else ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; @@ -1664,7 +1663,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if (handler_new_ref(hdl, ctrl, NULL, false, false)) { - kvfree(ctrl->p_dyn); + kvfree(ctrl->p_array); kvfree(ctrl); return NULL; } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 00828a4f9404..5ddd506ae7b9 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -203,7 +203,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @elem_size: The size in bytes of the control. * @new_elems: The number of elements in p_new. This is the same as @elems, * except for dynamic arrays. In that case it is in the range of - * 1 to @p_dyn_alloc_elems. + * 1 to @p_array_alloc_elems. * @dims: The size of each dimension. * @nr_of_dims:The number of dimensions in @dims. * @menu_skip_mask: The control's skip mask for menu controls. This makes it @@ -227,12 +227,11 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * not freed when the control is deleted. Should this be needed * then a new internal bitfield can be added to tell the framework * to free this pointer. - * @p_dyn: Pointer to the dynamically allocated array. Only valid if - * @is_dyn_array is true. - * @p_dyn_alloc_elems: The number of elements in the dynamically allocated - * array for both the cur and new values. So @p_dyn is actually - * sized for 2 * @p_dyn_alloc_elems * @elem_size. Only valid if - * @is_dyn_array is true. + * @p_array: Pointer to the allocated array. Only valid if @is_array is true. + * @p_array_alloc_elems: The number of elements in the allocated + * array for both the cur and new values. So @p_array is actually + * sized for 2 * @p_array_alloc_elems * @elem_size. Only valid if + * @is_array is true. * @cur: Structure to store the current value. * @cur.val: The control's current value, if the @type is represented via * a u32 integer (see &enum v4l2_ctrl_type). @@ -291,8 +290,8 @@ struct v4l2_ctrl { }; unsigned long flags; void *priv; - void *p_dyn; - u32 p_dyn_alloc_elems; + void *p_array; + u32 p_array_alloc_elems; s32 val; struct { s32 val; -- cgit v1.3-14-g43fede From 7392d87a9febb5f46f28d4704eb5636c5e22cdeb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Jul 2022 12:21:08 +0200 Subject: media: v4l2-ctrls: alloc arrays in ctrl_ref Also allocate space for arrays in struct ctrl_ref. This is in preparation for allowing to change the array size from a driver. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-api.c | 2 +- drivers/media/v4l2-core/v4l2-ctrls-core.c | 31 +++++++++++++++++++------------ include/media/v4l2-ctrls.h | 16 ++++++++-------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 1b90bd7c4010..6f1b72c59e8e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -467,7 +467,7 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, if (is_default) ret = def_to_user(cs->controls + idx, ref->ctrl); - else if (is_request && ref->p_req_dyn_enomem) + else if (is_request && ref->p_req_array_enomem) ret = -ENOMEM; else if (is_request && ref->p_req_valid) ret = req_to_user(cs->controls + idx, ref); diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 9871c77f559b..a004fea10da2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -1048,23 +1048,26 @@ void cur_to_new(struct v4l2_ctrl *ctrl) ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); } -static bool req_alloc_dyn_array(struct v4l2_ctrl_ref *ref, u32 elems) +static bool req_alloc_array(struct v4l2_ctrl_ref *ref, u32 elems) { void *tmp; - if (elems < ref->p_req_dyn_alloc_elems) + if (elems == ref->p_req_array_alloc_elems) + return true; + if (ref->ctrl->is_dyn_array && + elems < ref->p_req_array_alloc_elems) return true; tmp = kvmalloc(elems * ref->ctrl->elem_size, GFP_KERNEL); if (!tmp) { - ref->p_req_dyn_enomem = true; + ref->p_req_array_enomem = true; return false; } - ref->p_req_dyn_enomem = false; + ref->p_req_array_enomem = false; kvfree(ref->p_req.p); ref->p_req.p = tmp; - ref->p_req_dyn_alloc_elems = elems; + ref->p_req_array_alloc_elems = elems; return true; } @@ -1077,7 +1080,7 @@ void new_to_req(struct v4l2_ctrl_ref *ref) return; ctrl = ref->ctrl; - if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->new_elems)) + if (ctrl->is_array && !req_alloc_array(ref, ctrl->new_elems)) return; ref->p_req_elems = ctrl->new_elems; @@ -1094,7 +1097,7 @@ void cur_to_req(struct v4l2_ctrl_ref *ref) return; ctrl = ref->ctrl; - if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->elems)) + if (ctrl->is_array && !req_alloc_array(ref, ctrl->elems)) return; ref->p_req_elems = ctrl->elems; @@ -1123,14 +1126,18 @@ int req_to_new(struct v4l2_ctrl_ref *ref) return 0; } - /* Not a dynamic array, so just copy the request value */ - if (!ctrl->is_dyn_array) { + /* Not an array, so just copy the request value */ + if (!ctrl->is_array) { ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); return 0; } /* Sanity check, should never happen */ - if (WARN_ON(!ref->p_req_dyn_alloc_elems)) + if (WARN_ON(!ref->p_req_array_alloc_elems)) + return -ENOMEM; + + if (!ctrl->is_dyn_array && + ref->p_req_elems != ctrl->p_array_alloc_elems) return -ENOMEM; /* @@ -1243,7 +1250,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) /* Free all nodes */ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { list_del(&ref->node); - if (ref->p_req_dyn_alloc_elems) + if (ref->p_req_array_alloc_elems) kvfree(ref->p_req.p); kfree(ref); } @@ -1368,7 +1375,7 @@ int handler_new_ref(struct v4l2_ctrl_handler *hdl, if (hdl->error) return hdl->error; - if (allocate_req && !ctrl->is_dyn_array) + if (allocate_req && !ctrl->is_array) size_extra_req = ctrl->elems * ctrl->elem_size; new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL); if (!new_ref) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 5ddd506ae7b9..c7a082c319d4 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -318,15 +318,15 @@ struct v4l2_ctrl { * from a cluster with multiple controls twice (when the first * control of a cluster is applied, they all are). * @p_req_valid: If set, then p_req contains the control value for the request. - * @p_req_dyn_enomem: If set, then p_req is invalid since allocating space for - * a dynamic array failed. Attempting to read this value shall - * result in ENOMEM. Only valid if ctrl->is_dyn_array is true. - * @p_req_dyn_alloc_elems: The number of elements allocated for the dynamic - * array. Only valid if @p_req_valid and ctrl->is_dyn_array are + * @p_req_array_enomem: If set, then p_req is invalid since allocating space for + * an array failed. Attempting to read this value shall + * result in ENOMEM. Only valid if ctrl->is_array is true. + * @p_req_array_alloc_elems: The number of elements allocated for the + * array. Only valid if @p_req_valid and ctrl->is_array are * true. * @p_req_elems: The number of elements in @p_req. This is the same as * ctrl->elems, except for dynamic arrays. In that case it is in - * the range of 1 to @p_req_dyn_alloc_elems. Only valid if + * the range of 1 to @p_req_array_alloc_elems. Only valid if * @p_req_valid is true. * @p_req: If the control handler containing this control reference * is bound to a media request, then this points to the @@ -348,8 +348,8 @@ struct v4l2_ctrl_ref { bool from_other_dev; bool req_done; bool p_req_valid; - bool p_req_dyn_enomem; - u32 p_req_dyn_alloc_elems; + bool p_req_array_enomem; + u32 p_req_array_alloc_elems; u32 p_req_elems; union v4l2_ctrl_ptr p_req; }; -- cgit v1.3-14-g43fede From 0975274557d15ab9a01c0944f27065e6708a797c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Jul 2022 12:21:09 +0200 Subject: media: v4l2-ctrls: add v4l2_ctrl_modify_dimensions Add a new function to modify the dimensions of an array control. This is typically used if the array size depends on e.g. the currently selected video format. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-api.c | 36 ++++++++++++++++++++++ include/media/v4l2-ctrls.h | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 6f1b72c59e8e..878da8592106 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -989,6 +989,42 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, } EXPORT_SYMBOL(__v4l2_ctrl_modify_range); +int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl, + u32 dims[V4L2_CTRL_MAX_DIMS]) +{ + unsigned int elems = 1; + unsigned int i; + void *p_array; + + lockdep_assert_held(ctrl->handler->lock); + + if (!ctrl->is_array || ctrl->is_dyn_array) + return -EINVAL; + + for (i = 0; i < ctrl->nr_of_dims; i++) + elems *= dims[i]; + if (elems == 0) + return -EINVAL; + p_array = kvzalloc(2 * elems * ctrl->elem_size, GFP_KERNEL); + if (!p_array) + return -ENOMEM; + kvfree(ctrl->p_array); + ctrl->p_array_alloc_elems = elems; + ctrl->elems = elems; + ctrl->new_elems = elems; + ctrl->p_array = p_array; + ctrl->p_new.p = p_array; + ctrl->p_cur.p = p_array + elems * ctrl->elem_size; + for (i = 0; i < ctrl->nr_of_dims; i++) + ctrl->dims[i] = dims[i]; + for (i = 0; i < elems; i++) + ctrl->type_ops->init(ctrl, i, ctrl->p_cur); + cur_to_new(ctrl); + send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE); + return 0; +} +EXPORT_SYMBOL(__v4l2_ctrl_modify_dimensions); + /* Implement VIDIOC_QUERY_EXT_CTRL */ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c7a082c319d4..607960309579 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -957,6 +957,59 @@ static inline int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, return rval; } +/** + *__v4l2_ctrl_modify_dimensions() - Unlocked variant of v4l2_ctrl_modify_dimensions() + * + * @ctrl: The control to update. + * @dims: The control's new dimensions. + * + * Update the dimensions of an array control on the fly. The elements of the + * array are reset to their default value, even if the dimensions are + * unchanged. + * + * An error is returned if @dims is invalid for this control. + * + * The caller is responsible for acquiring the control handler mutex on behalf + * of __v4l2_ctrl_modify_dimensions(). + * + * Note: calling this function when the same control is used in pending requests + * is untested. It should work (a request with the wrong size of the control + * will drop that control silently), but it will be very confusing. + */ +int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl, + u32 dims[V4L2_CTRL_MAX_DIMS]); + +/** + * v4l2_ctrl_modify_dimensions() - Update the dimensions of an array control. + * + * @ctrl: The control to update. + * @dims: The control's new dimensions. + * + * Update the dimensions of an array control on the fly. The elements of the + * array are reset to their default value, even if the dimensions are + * unchanged. + * + * An error is returned if @dims is invalid for this control type. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + * + * Note: calling this function when the same control is used in pending requests + * is untested. It should work (a request with the wrong size of the control + * will drop that control silently), but it will be very confusing. + */ +static inline int v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl, + u32 dims[V4L2_CTRL_MAX_DIMS]) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_modify_dimensions(ctrl, dims); + v4l2_ctrl_unlock(ctrl); + + return rval; +} + /** * v4l2_ctrl_notify() - Function to set a notify callback for a control. * -- cgit v1.3-14-g43fede From 43cc0ec38131c10557c771760ffdfdb74a2da155 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Jul 2022 12:21:10 +0200 Subject: media: v4l2-ctrls: add change flag for when dimensions change Add a new V4L2_EVENT_CTRL_CH_DIMENSIONS change flag that is issued when the dimensions of an array change as a result of a __v4l2_ctrl_modify_dimensions() call. This will inform userspace that there are new dimensions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/vidioc-dqevent.rst | 5 +++++ Documentation/userspace-api/media/videodev2.h.rst.exceptions | 1 + drivers/media/v4l2-core/v4l2-ctrls-api.c | 3 ++- include/uapi/linux/videodev2.h | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst index 6eb40073c906..8db103760930 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst @@ -332,6 +332,11 @@ call. - 0x0004 - This control event was triggered because the minimum, maximum, step or the default value of the control changed. + * - ``V4L2_EVENT_CTRL_CH_DIMENSIONS`` + - 0x0008 + - This control event was triggered because the dimensions of the + control changed. Note that the number of dimensions remains the + same. .. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.5cm}| diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index 64b2c0b1f666..2a589d34b80e 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -514,6 +514,7 @@ replace define V4L2_EVENT_PRIVATE_START event-type replace define V4L2_EVENT_CTRL_CH_VALUE ctrl-changes-flags replace define V4L2_EVENT_CTRL_CH_FLAGS ctrl-changes-flags replace define V4L2_EVENT_CTRL_CH_RANGE ctrl-changes-flags +replace define V4L2_EVENT_CTRL_CH_DIMENSIONS ctrl-changes-flags replace define V4L2_EVENT_SRC_CH_RESOLUTION src-changes-flags diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 878da8592106..67fbdccda2d8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -1020,7 +1020,8 @@ int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl, for (i = 0; i < elems; i++) ctrl->type_ops->init(ctrl, i, ctrl->p_cur); cur_to_new(ctrl); - send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE); + send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE | + V4L2_EVENT_CTRL_CH_DIMENSIONS); return 0; } EXPORT_SYMBOL(__v4l2_ctrl_modify_dimensions); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 01e630f2ec78..c415ce5b6829 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -2435,6 +2435,7 @@ struct v4l2_event_vsync { #define V4L2_EVENT_CTRL_CH_VALUE (1 << 0) #define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1) #define V4L2_EVENT_CTRL_CH_RANGE (1 << 2) +#define V4L2_EVENT_CTRL_CH_DIMENSIONS (1 << 3) struct v4l2_event_ctrl { __u32 changes; -- cgit v1.3-14-g43fede From 6bc7643d1b9cf131f6ef98082548dec83f753fb8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Jul 2022 12:21:11 +0200 Subject: media: vivid: add pixel_array test control This control will change dimensions according to the source resolution. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-core.h | 5 ++++- drivers/media/test-drivers/vivid/vivid-ctrls.c | 14 ++++++++++++++ drivers/media/test-drivers/vivid/vivid-vid-cap.c | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index 176b72cb143b..bfcfb3515901 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -35,7 +35,9 @@ #define MAX_HEIGHT 2160 /* The minimum image width/height */ #define MIN_WIDTH 16 -#define MIN_HEIGHT 16 +#define MIN_HEIGHT MIN_WIDTH +/* Pixel Array control divider */ +#define PIXEL_ARRAY_DIV MIN_WIDTH /* The data_offset of plane 0 for the multiplanar formats */ #define PLANE0_DATA_OFFSET 128 @@ -227,6 +229,7 @@ struct vivid_dev { struct v4l2_ctrl *bitmask; struct v4l2_ctrl *int_menu; struct v4l2_ctrl *ro_int32; + struct v4l2_ctrl *pixel_array; struct v4l2_ctrl *test_pattern; struct v4l2_ctrl *colorspace; struct v4l2_ctrl *rgb_range_cap; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index a78d676575bc..92b1a7598470 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -35,6 +35,7 @@ #define VIVID_CID_AREA (VIVID_CID_CUSTOM_BASE + 11) #define VIVID_CID_RO_INTEGER (VIVID_CID_CUSTOM_BASE + 12) #define VIVID_CID_U32_DYN_ARRAY (VIVID_CID_CUSTOM_BASE + 13) +#define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -228,6 +229,18 @@ static const struct v4l2_ctrl_config vivid_ctrl_u8_4d_array = { .dims = { 2, 3, 4, 5 }, }; +static const struct v4l2_ctrl_config vivid_ctrl_u8_pixel_array = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_U8_PIXEL_ARRAY, + .name = "U8 Pixel Array", + .type = V4L2_CTRL_TYPE_U8, + .def = 0x80, + .min = 0x00, + .max = 0xff, + .step = 1, + .dims = { 640 / PIXEL_ARRAY_DIV, 360 / PIXEL_ARRAY_DIV }, +}; + static const char * const vivid_ctrl_menu_strings[] = { "Menu Item 0 (Skipped)", "Menu Item 1", @@ -1642,6 +1655,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_dyn_array, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL); + dev->pixel_array = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_pixel_array, NULL); if (dev->has_vid_cap) { /* Image Processing Controls */ diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b9caa4b26209..86b158eeb2d8 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -381,6 +381,7 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; + u32 dims[V4L2_CTRL_MAX_DIMS] = {}; unsigned size; u64 pixelclock; @@ -459,6 +460,9 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); tpg_s_pixel_aspect(&dev->tpg, vivid_get_pixel_aspect(dev)); tpg_update_mv_step(&dev->tpg); + dims[0] = roundup(dev->src_rect.width, PIXEL_ARRAY_DIV); + dims[1] = roundup(dev->src_rect.height, PIXEL_ARRAY_DIV); + v4l2_ctrl_modify_dimensions(dev->pixel_array, dims); } /* Map the field to something that is valid for the current input */ -- cgit v1.3-14-g43fede From 6b1aaa689348fecba304911e6bc89f3f5b0a4825 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 30 Jul 2022 17:48:36 +0200 Subject: media: v4l2-ctrls: optimize type_ops for arrays Initializing arrays and validating or checking for equality of arrays is suboptimal since it does this per element. Change the ops to operate on the whole payload to speed up array operations. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-api.c | 19 +---- drivers/media/v4l2-core/v4l2-ctrls-core.c | 132 +++++++++++++++++++++--------- include/media/v4l2-ctrls.h | 6 +- 3 files changed, 99 insertions(+), 58 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 67fbdccda2d8..a8c354ad3d23 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -89,10 +89,7 @@ static int req_to_user(struct v4l2_ext_control *c, /* Helper function: copy the initial control value back to the caller */ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) { - int idx; - - for (idx = 0; idx < ctrl->elems; idx++) - ctrl->type_ops->init(ctrl, idx, ctrl->p_new); + ctrl->type_ops->init(ctrl, 0, ctrl->elems, ctrl->p_new); return ptr_to_user(c, ctrl, ctrl->p_new); } @@ -122,7 +119,6 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) if (ctrl->is_ptr && !ctrl->is_string) { unsigned int elems = c->size / ctrl->elem_size; - unsigned int idx; if (copy_from_user(ctrl->p_new.p, c->ptr, c->size)) return -EFAULT; @@ -130,8 +126,7 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) if (ctrl->is_dyn_array) ctrl->new_elems = elems; else if (ctrl->is_array) - for (idx = elems; idx < ctrl->elems; idx++) - ctrl->type_ops->init(ctrl, idx, ctrl->p_new); + ctrl->type_ops->init(ctrl, elems, ctrl->elems, ctrl->p_new); return 0; } @@ -499,12 +494,7 @@ EXPORT_SYMBOL(v4l2_g_ext_ctrls); /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) { - unsigned int idx; - int err = 0; - - for (idx = 0; !err && idx < ctrl->new_elems; idx++) - err = ctrl->type_ops->validate(ctrl, idx, p_new); - return err; + return ctrl->type_ops->validate(ctrl, ctrl->new_elems, p_new); } /* Validate controls. */ @@ -1017,8 +1007,7 @@ int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl, ctrl->p_cur.p = p_array + elems * ctrl->elem_size; for (i = 0; i < ctrl->nr_of_dims; i++) ctrl->dims[i] = dims[i]; - for (i = 0; i < elems; i++) - ctrl->type_ops->init(ctrl, i, ctrl->p_cur); + ctrl->type_ops->init(ctrl, 0, elems, ctrl->p_cur); cur_to_new(ctrl); send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE | V4L2_EVENT_CTRL_CH_DIMENSIONS); diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index a004fea10da2..396772bf7ccd 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -65,31 +65,27 @@ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } -static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, +static bool std_equal(const struct v4l2_ctrl *ctrl, u32 elems, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) { + unsigned int i; + switch (ctrl->type) { case V4L2_CTRL_TYPE_BUTTON: return false; case V4L2_CTRL_TYPE_STRING: - idx *= ctrl->elem_size; - /* strings are always 0-terminated */ - return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); - case V4L2_CTRL_TYPE_INTEGER64: - return ptr1.p_s64[idx] == ptr2.p_s64[idx]; - case V4L2_CTRL_TYPE_U8: - return ptr1.p_u8[idx] == ptr2.p_u8[idx]; - case V4L2_CTRL_TYPE_U16: - return ptr1.p_u16[idx] == ptr2.p_u16[idx]; - case V4L2_CTRL_TYPE_U32: - return ptr1.p_u32[idx] == ptr2.p_u32[idx]; + for (i = 0; i < elems; i++) { + unsigned int idx = i * ctrl->elem_size; + + /* strings are always 0-terminated */ + if (strcmp(ptr1.p_char + idx, ptr2.p_char + idx)) + return false; + } + return true; default: - if (ctrl->is_int) - return ptr1.p_s32[idx] == ptr2.p_s32[idx]; - idx *= ctrl->elem_size; - return !memcmp(ptr1.p_const + idx, ptr2.p_const + idx, - ctrl->elem_size); + return !memcmp(ptr1.p_const, ptr2.p_const, + elems * ctrl->elem_size); } } @@ -181,40 +177,70 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, } } -static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, +static void std_init(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 tot_elems, union v4l2_ctrl_ptr ptr) { + unsigned int i; + u32 elems = tot_elems - from_idx; + + if (from_idx >= tot_elems) + return; + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - idx *= ctrl->elem_size; - memset(ptr.p_char + idx, ' ', ctrl->minimum); - ptr.p_char[idx + ctrl->minimum] = '\0'; + for (i = from_idx; i < tot_elems; i++) { + unsigned int offset = i * ctrl->elem_size; + + memset(ptr.p_char + offset, ' ', ctrl->minimum); + ptr.p_char[offset + ctrl->minimum] = '\0'; + } break; case V4L2_CTRL_TYPE_INTEGER64: - ptr.p_s64[idx] = ctrl->default_value; + if (ctrl->default_value) { + for (i = from_idx; i < tot_elems; i++) + ptr.p_s64[i] = ctrl->default_value; + } else { + memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64)); + } break; case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BOOLEAN: - ptr.p_s32[idx] = ctrl->default_value; + if (ctrl->default_value) { + for (i = from_idx; i < tot_elems; i++) + ptr.p_s32[i] = ctrl->default_value; + } else { + memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); + } break; case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - ptr.p_s32[idx] = 0; + memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); break; case V4L2_CTRL_TYPE_U8: - ptr.p_u8[idx] = ctrl->default_value; + memset(ptr.p_u8 + from_idx, ctrl->default_value, elems); break; case V4L2_CTRL_TYPE_U16: - ptr.p_u16[idx] = ctrl->default_value; + if (ctrl->default_value) { + for (i = from_idx; i < tot_elems; i++) + ptr.p_u16[i] = ctrl->default_value; + } else { + memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16)); + } break; case V4L2_CTRL_TYPE_U32: - ptr.p_u32[idx] = ctrl->default_value; + if (ctrl->default_value) { + for (i = from_idx; i < tot_elems; i++) + ptr.p_u32[i] = ctrl->default_value; + } else { + memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32)); + } break; default: - std_init_compound(ctrl, idx, ptr); + for (i = from_idx; i < tot_elems; i++) + std_init_compound(ctrl, i, ptr); break; } } @@ -895,8 +921,8 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, return 0; } -static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, - union v4l2_ctrl_ptr ptr) +static int std_validate_elem(const struct v4l2_ctrl *ctrl, u32 idx, + union v4l2_ctrl_ptr ptr) { size_t len; u64 offset; @@ -966,6 +992,37 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, } } +static int std_validate(const struct v4l2_ctrl *ctrl, u32 elems, + union v4l2_ctrl_ptr ptr) +{ + unsigned int i; + int ret = 0; + + switch ((u32)ctrl->type) { + case V4L2_CTRL_TYPE_U8: + if (ctrl->maximum == 0xff && ctrl->minimum == 0 && ctrl->step == 1) + return 0; + break; + case V4L2_CTRL_TYPE_U16: + if (ctrl->maximum == 0xffff && ctrl->minimum == 0 && ctrl->step == 1) + return 0; + break; + case V4L2_CTRL_TYPE_U32: + if (ctrl->maximum == 0xffffffff && ctrl->minimum == 0 && ctrl->step == 1) + return 0; + break; + + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + memset(ptr.p_s32, 0, elems * sizeof(s32)); + return 0; + } + + for (i = 0; !ret && i < elems; i++) + ret = std_validate_elem(ctrl, i, ptr); + return ret; +} + static const struct v4l2_ctrl_type_ops std_type_ops = { .equal = std_equal, .init = std_init, @@ -1449,7 +1506,6 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned elems = 1; bool is_array; unsigned tot_ctrl_size; - unsigned idx; void *data; int err; @@ -1664,10 +1720,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, memcpy(ctrl->p_def.p, p_def.p_const, elem_size); } - for (idx = 0; idx < elems; idx++) { - ctrl->type_ops->init(ctrl, idx, ctrl->p_cur); - ctrl->type_ops->init(ctrl, idx, ctrl->p_new); - } + ctrl->type_ops->init(ctrl, 0, elems, ctrl->p_cur); + cur_to_new(ctrl); if (handler_new_ref(hdl, ctrl, NULL, false, false)) { kvfree(ctrl->p_array); @@ -1984,7 +2038,6 @@ void update_from_auto_cluster(struct v4l2_ctrl *master) static int cluster_changed(struct v4l2_ctrl *master) { bool changed = false; - unsigned int idx; int i; for (i = 0; i < master->ncontrols; i++) { @@ -2010,10 +2063,9 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl->elems != ctrl->new_elems) ctrl_changed = true; - - for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) - ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, - ctrl->p_cur, ctrl->p_new); + if (!ctrl_changed) + ctrl_changed = !ctrl->type_ops->equal(ctrl, + ctrl->elems, ctrl->p_cur, ctrl->p_new); ctrl->has_changed = ctrl_changed; changed |= ctrl->has_changed; } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 607960309579..879bdde5131b 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -128,13 +128,13 @@ struct v4l2_ctrl_ops { * otherwise. */ struct v4l2_ctrl_type_ops { - bool (*equal)(const struct v4l2_ctrl *ctrl, u32 idx, + bool (*equal)(const struct v4l2_ctrl *ctrl, u32 elems, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2); - void (*init)(const struct v4l2_ctrl *ctrl, u32 idx, + void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 tot_elems, union v4l2_ctrl_ptr ptr); void (*log)(const struct v4l2_ctrl *ctrl); - int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx, + int (*validate)(const struct v4l2_ctrl *ctrl, u32 elems, union v4l2_ctrl_ptr ptr); }; -- cgit v1.3-14-g43fede From f1739ec4c778f316fd3d0909408d23679cd77ed6 Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:37 +0200 Subject: media: v4l2-ctrls: Export default v4l2_ctrl_type_ops callbacks Export the callback functions of the default v4l2 control type operations such as a driver defining its own operations could reuse some of them. Signed-off-by: Xavier Roumegue Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-core.c | 30 ++++++++++--------- include/media/v4l2-ctrls.h | 48 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 396772bf7ccd..01f00093f259 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -65,9 +65,8 @@ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } -static bool std_equal(const struct v4l2_ctrl *ctrl, u32 elems, - union v4l2_ctrl_ptr ptr1, - union v4l2_ctrl_ptr ptr2) +bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems, + union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) { unsigned int i; @@ -88,6 +87,7 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 elems, elems * ctrl->elem_size); } } +EXPORT_SYMBOL(v4l2_ctrl_type_op_equal); /* Default intra MPEG-2 quantisation coefficients, from the specification. */ static const u8 mpeg2_intra_quant_matrix[64] = { @@ -177,8 +177,8 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, } } -static void std_init(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 tot_elems, - union v4l2_ctrl_ptr ptr) +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, + u32 tot_elems, union v4l2_ctrl_ptr ptr) { unsigned int i; u32 elems = tot_elems - from_idx; @@ -244,8 +244,9 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 tot_elems, break; } } +EXPORT_SYMBOL(v4l2_ctrl_type_op_init); -static void std_log(const struct v4l2_ctrl *ctrl) +void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) { union v4l2_ctrl_ptr ptr = ctrl->p_cur; @@ -353,6 +354,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) break; } } +EXPORT_SYMBOL(v4l2_ctrl_type_op_log); /* * Round towards the closest legal value. Be careful when we are @@ -546,7 +548,8 @@ validate_vp9_frame(struct v4l2_ctrl_vp9_frame *frame) /* * Compound controls validation requires setting unused fields/flags to zero - * in order to properly detect unchanged controls with std_equal's memcmp. + * in order to properly detect unchanged controls with v4l2_ctrl_type_op_equal's + * memcmp. */ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) @@ -992,8 +995,8 @@ static int std_validate_elem(const struct v4l2_ctrl *ctrl, u32 idx, } } -static int std_validate(const struct v4l2_ctrl *ctrl, u32 elems, - union v4l2_ctrl_ptr ptr) +int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl, u32 elems, + union v4l2_ctrl_ptr ptr) { unsigned int i; int ret = 0; @@ -1022,12 +1025,13 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 elems, ret = std_validate_elem(ctrl, i, ptr); return ret; } +EXPORT_SYMBOL(v4l2_ctrl_type_op_validate); static const struct v4l2_ctrl_type_ops std_type_ops = { - .equal = std_equal, - .init = std_init, - .log = std_log, - .validate = std_validate, + .equal = v4l2_ctrl_type_op_equal, + .init = v4l2_ctrl_type_op_init, + .log = v4l2_ctrl_type_op_log, + .validate = v4l2_ctrl_type_op_validate, }; void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 879bdde5131b..b76a0714d425 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -1538,4 +1538,52 @@ int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd); int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ctrl_ops, const struct v4l2_fwnode_device_properties *p); + +/** + * v4l2_ctrl_type_op_equal - Default v4l2_ctrl_type_ops equal callback. + * + * @ctrl: The v4l2_ctrl pointer. + * @elems: The number of elements to compare. + * @ptr1: A v4l2 control value. + * @ptr2: A v4l2 control value. + * + * Return: true if values are equal, otherwise false. + */ +bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl, u32 elems, + union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2); + +/** + * v4l2_ctrl_type_op_init - Default v4l2_ctrl_type_ops init callback. + * + * @ctrl: The v4l2_ctrl pointer. + * @from_idx: Starting element index. + * @elems: The number of elements to initialize. + * @ptr: The v4l2 control value. + * + * Return: void + */ +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, + u32 elems, union v4l2_ctrl_ptr ptr); + +/** + * v4l2_ctrl_type_op_log - Default v4l2_ctrl_type_ops log callback. + * + * @ctrl: The v4l2_ctrl pointer. + * + * Return: void + */ +void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl); + +/** + * v4l2_ctrl_type_op_validate - Default v4l2_ctrl_type_ops validate callback. + * + * @ctrl: The v4l2_ctrl pointer. + * @elems: The number of elements in the control. + * @ptr: The v4l2 control value. + * + * Return: 0 on success, a negative error code on failure. + */ +int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl, u32 elems, + union v4l2_ctrl_ptr ptr); + #endif -- cgit v1.3-14-g43fede From bdbb016da6eeec16b6e9f54d759a0cdc7ebe090e Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:38 +0200 Subject: media: Documentation: dw100: Add user documentation for the DW100 driver Add user documentation for the DW100 driver. while at it, replace spaces with tab on drivers list. Signed-off-by: Xavier Roumegue Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/drivers/dw100.rst | 69 ++++++++++++++++++++++ .../userspace-api/media/drivers/index.rst | 1 + 2 files changed, 70 insertions(+) create mode 100644 Documentation/userspace-api/media/drivers/dw100.rst diff --git a/Documentation/userspace-api/media/drivers/dw100.rst b/Documentation/userspace-api/media/drivers/dw100.rst new file mode 100644 index 000000000000..1ca6fa55f539 --- /dev/null +++ b/Documentation/userspace-api/media/drivers/dw100.rst @@ -0,0 +1,69 @@ +.. SPDX-License-Identifier: GPL-2.0 + +DW100 dewarp driver +=================== + +The Vivante DW100 Dewarp Processor IP core found on i.MX8MP SoC applies a +programmable geometrical transformation on the input image to correct distortion +introduced by lenses. + +The transformation function is exposed by the hardware as a grid map with 16x16 +pixel macroblocks indexed using X, Y vertex coordinates. +:: + + Image width + <---------------------------------------> + + ^ .-------.-------.-------.-------.-------. + | | 16x16 | | | | | + I | | pixel | | | | | + m | | block | | | | | + a | .-------.-------.-------.-------.-------. + g | | | | | | | + e | | | | | | | + | | | | | | | + h | .-------.-------.-------.-------.-------. + e | | | | | | | + i | | | | | | | + g | | | | | | | + h | .-------.-------.-------.-------.-------. + t | | | | | | | + | | | | | | | + | | | | | | | + v '-------'-------'-------'-------'-------' + + Grid of Image Blocks for Dewarping Map + + +Each x, y coordinate register uses 16 bits to record the coordinate address in +an unsigned 12.4 fixed point format (UQ12.4). +:: + + .----------------------.--------..----------------------.--------. + | 31~20 | 19~16 || 15~4 | 3~0 | + | (integer) | (frac) || (integer) | (frac) | + '----------------------'--------''----------------------'--------' + <-------------------------------><-------------------------------> + Y coordinate X coordinate + + Remap Register Layout + +The dewarping map is set from applications using the +V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP control. The control contains +an array of u32 values storing (x, y) destination coordinates for each +vertex of the grid. The x coordinate is stored in the 16 LSBs and the y +coordinate in the 16 MSBs. + +The number of elements in the array must match the image size: + +.. code-block:: C + + elems = (DIV_ROUND_UP(width, 16) + 1) * (DIV_ROUND_UP(height, 16) + 1); + +If the control has not been set by the application, the driver uses an identity +map. + +More details on the DW100 hardware operations can be found in +*chapter 13.15 DeWarp* of IMX8MP_ reference manual. + +.. _IMX8MP: https://www.nxp.com/webapp/Download?colCode=IMX8MPRM diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst index 1a9038f5f9fa..32f82aed47d9 100644 --- a/Documentation/userspace-api/media/drivers/index.rst +++ b/Documentation/userspace-api/media/drivers/index.rst @@ -33,6 +33,7 @@ For more details see the file COPYING in the source distribution of Linux. ccs cx2341x-uapi + dw100 imx-uapi max2175 meye-uapi -- cgit v1.3-14-g43fede From a41c4088cf431a9f2458f4e54fbbf3113a1bd063 Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:39 +0200 Subject: media: v4l: uapi: Add user control base for DW100 controls Add a control base for DW100 driver controls, and reserve 16 controls. Signed-off-by: Xavier Roumegue Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-controls.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 5f46bf4a570c..87fa476428ee 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -225,6 +225,12 @@ enum v4l2_colorfx { */ #define V4L2_CID_USER_ISL7998X_BASE (V4L2_CID_USER_BASE + 0x1180) +/* + * The base for DW100 driver controls. + * We reserve 16 controls for this driver. + */ +#define V4L2_CID_USER_DW100_BASE (V4L2_CID_USER_BASE + 0x1190) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -- cgit v1.3-14-g43fede From 9d5c3c06980510b27e8f7ff033a21120e42c9715 Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:40 +0200 Subject: media: uapi: Add a control for DW100 driver The DW100 driver gets the dewarping mapping as a binary blob from the userspace application through a custom control. The blob format is hardware specific so create a dedicated control for this purpose. Signed-off-by: Xavier Roumegue Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/drivers/dw100.rst | 15 +++++++++++++++ include/uapi/linux/dw100.h | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 include/uapi/linux/dw100.h diff --git a/Documentation/userspace-api/media/drivers/dw100.rst b/Documentation/userspace-api/media/drivers/dw100.rst index 1ca6fa55f539..fceea6ece622 100644 --- a/Documentation/userspace-api/media/drivers/dw100.rst +++ b/Documentation/userspace-api/media/drivers/dw100.rst @@ -66,4 +66,19 @@ map. More details on the DW100 hardware operations can be found in *chapter 13.15 DeWarp* of IMX8MP_ reference manual. +The Vivante DW100 m2m driver implements the following driver-specific control: + +``V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP (__u32 array)`` + Specifies to DW100 driver its dewarping map (aka LUT) blob as described in + *chapter 13.15.2.3 Dewarping Remap* of IMX8MP_ reference manual as an U32 + dynamic array. The image is divided into many small 16x16 blocks. If the + width/height of the image is not divisible by 16, the size of the + rightmost/bottommost block is the remainder. The dewarping map only saves + the vertex coordinates of the block. The dewarping grid map is comprised of + vertex coordinates for x and y. Each x, y coordinate register uses 16 bits + (UQ12.4) to record the coordinate address, with the Y coordinate in the + upper bits and X in the lower bits. The driver modifies the dimensions of + this control when the sink format is changed, to reflect the new input + resolution. + .. _IMX8MP: https://www.nxp.com/webapp/Download?colCode=IMX8MPRM diff --git a/include/uapi/linux/dw100.h b/include/uapi/linux/dw100.h new file mode 100644 index 000000000000..3356496edd6b --- /dev/null +++ b/include/uapi/linux/dw100.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* Copyright 2022 NXP */ + +#ifndef __UAPI_DW100_H__ +#define __UAPI_DW100_H__ + +#include + +/* + * Check Documentation/userspace-api/media/drivers/dw100.rst for control details. + */ +#define V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP (V4L2_CID_USER_DW100_BASE + 1) + +#endif -- cgit v1.3-14-g43fede From 1301663c1f350cad2910da83a20cc6693ff44ee9 Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:41 +0200 Subject: media: dt-bindings: media: Add i.MX8MP DW100 binding Add DT binding documentation for the Vivante DW100 dewarper engine found on NXP i.MX8MP SoC Signed-off-by: Xavier Roumegue Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/nxp,dw100.yaml | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/nxp,dw100.yaml diff --git a/Documentation/devicetree/bindings/media/nxp,dw100.yaml b/Documentation/devicetree/bindings/media/nxp,dw100.yaml new file mode 100644 index 000000000000..21910ff0e1c3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/nxp,dw100.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/nxp,dw100.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8MP DW100 Dewarper core + +maintainers: + - Xavier Roumegue + +description: |- + The Dewarp Engine provides high-performance dewarp processing for the + correction of the distortion that is introduced in images produced by fisheye + and wide angle lenses. It is implemented with a line/tile-cache based + architecture. With configurable address mapping look up tables and per tile + processing, it successfully generates a corrected output image. + The engine can be used to perform scaling, cropping and pixel format + conversion. + +properties: + compatible: + enum: + - nxp,imx8mp-dw100 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: The AXI clock + - description: The AHB clock + + clock-names: + items: + - const: axi + - const: ahb + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + dewarp: dwe@32e30000 { + compatible = "nxp,imx8mp-dw100"; + reg = <0x32e30000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MP_CLK_MEDIA_AXI_ROOT>, + <&clk IMX8MP_CLK_MEDIA_APB_ROOT>; + clock-names = "axi", "ahb"; + power-domains = <&media_blk_ctrl IMX8MP_MEDIABLK_PD_DWE>; + }; -- cgit v1.3-14-g43fede From cb6d000fcaa6e2ee0e01937364c686690329bf5e Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:42 +0200 Subject: media: dw100: Add i.MX8MP dw100 dewarper driver Add a V4L2 mem-to-mem driver for the Vivante DW100 Dewarp Processor IP core found on i.MX8MP SoC. The processor core applies a programmable geometrical transformation on input images to correct distorsion introduced by lenses. The transformation function is exposed as a grid map with 16x16 pixel macroblocks indexed using X, Y vertex coordinates. The dewarping map can be set from application through a dedicated v4l2 control. If not set or invalid, the driver computes an identity map prior to starting the processing engine. The driver supports scaling, cropping and pixel format conversion. Signed-off-by: Xavier Roumegue Reviewed-by: Ezequiel Garcia Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/nxp/Kconfig | 1 + drivers/media/platform/nxp/Makefile | 1 + drivers/media/platform/nxp/dw100/Kconfig | 16 + drivers/media/platform/nxp/dw100/Makefile | 3 + drivers/media/platform/nxp/dw100/dw100.c | 1706 +++++++++++++++++++++++++ drivers/media/platform/nxp/dw100/dw100_regs.h | 117 ++ 6 files changed, 1844 insertions(+) create mode 100644 drivers/media/platform/nxp/dw100/Kconfig create mode 100644 drivers/media/platform/nxp/dw100/Makefile create mode 100644 drivers/media/platform/nxp/dw100/dw100.c create mode 100644 drivers/media/platform/nxp/dw100/dw100_regs.h diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig index 1ac0a6e91111..4c76656e353e 100644 --- a/drivers/media/platform/nxp/Kconfig +++ b/drivers/media/platform/nxp/Kconfig @@ -51,4 +51,5 @@ config VIDEO_MX2_EMMAPRP memory to memory. Operations include resizing and format conversion. +source "drivers/media/platform/nxp/dw100/Kconfig" source "drivers/media/platform/nxp/imx-jpeg/Kconfig" diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile index efc38c6578ce..22ba28ac6d63 100644 --- a/drivers/media/platform/nxp/Makefile +++ b/drivers/media/platform/nxp/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-y += dw100/ obj-y += imx-jpeg/ obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o diff --git a/drivers/media/platform/nxp/dw100/Kconfig b/drivers/media/platform/nxp/dw100/Kconfig new file mode 100644 index 000000000000..cd4531bb3110 --- /dev/null +++ b/drivers/media/platform/nxp/dw100/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_DW100 + tristate "NXP i.MX DW100 dewarper" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_MXC || COMPILE_TEST + select MEDIA_CONTROLLER + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + help + DW100 is a memory-to-memory engine performing geometrical + transformation on source images through a programmable dewarping map. + + To compile this driver as a module, choose M here: the module + will be called dw100. diff --git a/drivers/media/platform/nxp/dw100/Makefile b/drivers/media/platform/nxp/dw100/Makefile new file mode 100644 index 000000000000..49db80589e9a --- /dev/null +++ b/drivers/media/platform/nxp/dw100/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_VIDEO_DW100) += dw100.o diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c new file mode 100644 index 000000000000..94518f0e486b --- /dev/null +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -0,0 +1,1706 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DW100 Hardware dewarper + * + * Copyright 2022 NXP + * Author: Xavier Roumegue (xavier.roumegue@oss.nxp.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "dw100_regs.h" + +#define DRV_NAME "dw100" + +#define DW100_MIN_W 176u +#define DW100_MIN_H 144u +#define DW100_MAX_W 4096u +#define DW100_MAX_H 3072u +#define DW100_ALIGN_W 3 +#define DW100_ALIGN_H 3 + +#define DW100_BLOCK_SIZE 16 + +#define DW100_DEF_W 640u +#define DW100_DEF_H 480u +#define DW100_DEF_LUT_W (DIV_ROUND_UP(DW100_DEF_W, DW100_BLOCK_SIZE) + 1) +#define DW100_DEF_LUT_H (DIV_ROUND_UP(DW100_DEF_H, DW100_BLOCK_SIZE) + 1) + +/* + * 16 controls have been reserved for this driver for future extension, but + * let's limit the related driver allocation to the effective number of controls + * in use. + */ +#define DW100_MAX_CTRLS 1 +#define DW100_CTRL_DEWARPING_MAP 0 + +enum { + DW100_QUEUE_SRC = 0, + DW100_QUEUE_DST = 1, +}; + +enum { + DW100_FMT_CAPTURE = BIT(0), + DW100_FMT_OUTPUT = BIT(1), +}; + +struct dw100_device { + struct platform_device *pdev; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct media_device mdev; + /* Video device lock */ + struct mutex vfd_mutex; + void __iomem *mmio; + struct clk_bulk_data *clks; + int num_clks; + struct dentry *debugfs_root; +}; + +struct dw100_q_data { + struct v4l2_pix_format_mplane pix_fmt; + unsigned int sequence; + const struct dw100_fmt *fmt; + struct v4l2_rect crop; +}; + +struct dw100_ctx { + struct v4l2_fh fh; + struct dw100_device *dw_dev; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *ctrls[DW100_MAX_CTRLS]; + /* per context m2m queue lock */ + struct mutex vq_mutex; + + /* Look Up Table for pixel remapping */ + unsigned int *map; + dma_addr_t map_dma; + size_t map_size; + unsigned int map_width; + unsigned int map_height; + bool user_map_is_set; + + /* Source and destination queue data */ + struct dw100_q_data q_data[2]; +}; + +static const struct v4l2_frmsize_stepwise dw100_frmsize_stepwise = { + .min_width = DW100_MIN_W, + .min_height = DW100_MIN_H, + .max_width = DW100_MAX_W, + .max_height = DW100_MAX_H, + .step_width = 1UL << DW100_ALIGN_W, + .step_height = 1UL << DW100_ALIGN_H, +}; + +static const struct dw100_fmt { + u32 fourcc; + u32 types; + u32 reg_format; + bool reg_swap_uv; +} formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, + .reg_swap_uv = false, + }, { + .fourcc = V4L2_PIX_FMT_NV16M, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, + .reg_swap_uv = false, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .types = DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, + .reg_swap_uv = true, + }, { + .fourcc = V4L2_PIX_FMT_NV61M, + .types = DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, + .reg_swap_uv = true, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED, + .reg_swap_uv = false, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED, + .reg_swap_uv = true, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, + .reg_swap_uv = false, + }, { + .fourcc = V4L2_PIX_FMT_NV12M, + .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, + .reg_swap_uv = false, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .types = DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, + .reg_swap_uv = true, + }, { + .fourcc = V4L2_PIX_FMT_NV21M, + .types = DW100_FMT_CAPTURE, + .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, + .reg_swap_uv = true, + }, +}; + +static inline int to_dw100_fmt_type(enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return DW100_FMT_OUTPUT; + else + return DW100_FMT_CAPTURE; +} + +static const struct dw100_fmt *dw100_find_pixel_format(u32 pixel_format, + int fmt_type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + const struct dw100_fmt *fmt = &formats[i]; + + if (fmt->fourcc == pixel_format && fmt->types & fmt_type) + return fmt; + } + + return NULL; +} + +static const struct dw100_fmt *dw100_find_format(struct v4l2_format *f) +{ + return dw100_find_pixel_format(f->fmt.pix_mp.pixelformat, + to_dw100_fmt_type(f->type)); +} + +static inline u32 dw100_read(struct dw100_device *dw_dev, u32 reg) +{ + return readl(dw_dev->mmio + reg); +} + +static inline void dw100_write(struct dw100_device *dw_dev, u32 reg, u32 val) +{ + writel(val, dw_dev->mmio + reg); +} + +static inline int dw100_dump_regs(struct seq_file *m) +{ + struct dw100_device *dw_dev = m->private; +#define __DECLARE_REG(x) { #x, x } + unsigned int i; + static const struct reg_desc { + const char * const name; + unsigned int addr; + } dw100_regs[] = { + __DECLARE_REG(DW100_DEWARP_ID), + __DECLARE_REG(DW100_DEWARP_CTRL), + __DECLARE_REG(DW100_MAP_LUT_ADDR), + __DECLARE_REG(DW100_MAP_LUT_SIZE), + __DECLARE_REG(DW100_MAP_LUT_ADDR2), + __DECLARE_REG(DW100_MAP_LUT_SIZE2), + __DECLARE_REG(DW100_SRC_IMG_Y_BASE), + __DECLARE_REG(DW100_SRC_IMG_UV_BASE), + __DECLARE_REG(DW100_SRC_IMG_SIZE), + __DECLARE_REG(DW100_SRC_IMG_STRIDE), + __DECLARE_REG(DW100_DST_IMG_Y_BASE), + __DECLARE_REG(DW100_DST_IMG_UV_BASE), + __DECLARE_REG(DW100_DST_IMG_SIZE), + __DECLARE_REG(DW100_DST_IMG_STRIDE), + __DECLARE_REG(DW100_DST_IMG_Y_SIZE1), + __DECLARE_REG(DW100_DST_IMG_UV_SIZE1), + __DECLARE_REG(DW100_SRC_IMG_Y_BASE2), + __DECLARE_REG(DW100_SRC_IMG_UV_BASE2), + __DECLARE_REG(DW100_SRC_IMG_SIZE2), + __DECLARE_REG(DW100_SRC_IMG_STRIDE2), + __DECLARE_REG(DW100_DST_IMG_Y_BASE2), + __DECLARE_REG(DW100_DST_IMG_UV_BASE2), + __DECLARE_REG(DW100_DST_IMG_SIZE2), + __DECLARE_REG(DW100_DST_IMG_STRIDE2), + __DECLARE_REG(DW100_DST_IMG_Y_SIZE2), + __DECLARE_REG(DW100_DST_IMG_UV_SIZE2), + __DECLARE_REG(DW100_SWAP_CONTROL), + __DECLARE_REG(DW100_VERTICAL_SPLIT_LINE), + __DECLARE_REG(DW100_HORIZON_SPLIT_LINE), + __DECLARE_REG(DW100_SCALE_FACTOR), + __DECLARE_REG(DW100_ROI_START), + __DECLARE_REG(DW100_BOUNDARY_PIXEL), + __DECLARE_REG(DW100_INTERRUPT_STATUS), + __DECLARE_REG(DW100_BUS_CTRL), + __DECLARE_REG(DW100_BUS_CTRL1), + __DECLARE_REG(DW100_BUS_TIME_OUT_CYCLE), + }; + + for (i = 0; i < ARRAY_SIZE(dw100_regs); i++) + seq_printf(m, "%s: %#x\n", dw100_regs[i].name, + dw100_read(dw_dev, dw100_regs[i].addr)); + + return 0; +} + +static inline struct dw100_ctx *dw100_file2ctx(struct file *file) +{ + return container_of(file->private_data, struct dw100_ctx, fh); +} + +static struct dw100_q_data *dw100_get_q_data(struct dw100_ctx *ctx, + enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return &ctx->q_data[DW100_QUEUE_SRC]; + else + return &ctx->q_data[DW100_QUEUE_DST]; +} + +static u32 dw100_get_n_vertices_from_length(u32 length) +{ + return DIV_ROUND_UP(length, DW100_BLOCK_SIZE) + 1; +} + +static u16 dw100_map_convert_to_uq12_4(u32 a) +{ + return (u16)((a & 0xfff) << 4); +} + +static u32 dw100_map_format_coordinates(u16 xq, u16 yq) +{ + return (u32)((yq << 16) | xq); +} + +static u32 *dw100_get_user_map(struct dw100_ctx *ctx) +{ + struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP]; + + return ctrl->p_cur.p_u32; +} + +/* + * Create the dewarp map used by the hardware from the V4L2 control values which + * have been initialized with an identity map or set by the application. + */ +static int dw100_create_mapping(struct dw100_ctx *ctx) +{ + u32 *user_map; + + if (ctx->map) + dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, + ctx->map, ctx->map_dma); + + ctx->map = dma_alloc_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, + &ctx->map_dma, GFP_KERNEL); + + if (!ctx->map) + return -ENOMEM; + + user_map = dw100_get_user_map(ctx); + memcpy(ctx->map, user_map, ctx->map_size); + + dev_dbg(&ctx->dw_dev->pdev->dev, + "%ux%u %s mapping created (d:%pad-c:%p) for stream %ux%u->%ux%u\n", + ctx->map_width, ctx->map_height, + ctx->user_map_is_set ? "user" : "identity", + &ctx->map_dma, ctx->map, + ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width, + ctx->q_data[DW100_QUEUE_DST].pix_fmt.height, + ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width, + ctx->q_data[DW100_QUEUE_DST].pix_fmt.height); + + return 0; +} + +static void dw100_destroy_mapping(struct dw100_ctx *ctx) +{ + if (ctx->map) { + dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, + ctx->map, ctx->map_dma); + ctx->map = NULL; + } +} + +static int dw100_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw100_ctx *ctx = + container_of(ctrl->handler, struct dw100_ctx, hdl); + + switch (ctrl->id) { + case V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP: + ctx->user_map_is_set = true; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops dw100_ctrl_ops = { + .s_ctrl = dw100_s_ctrl, +}; + +/* + * Initialize the dewarping map with an identity mapping. + * + * A 16 pixels cell size grid is mapped on the destination image. + * The last cells width/height might be lesser than 16 if the destination image + * width/height is not divisible by 16. This dewarping grid map specifies the + * source image pixel location (x, y) on each grid intersection point. + * Bilinear interpolation is used to compute inner cell points locations. + * + * The coordinates are saved in UQ12.4 fixed point format. + */ +static void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl, + u32 from_idx, u32 elems, + union v4l2_ctrl_ptr ptr) +{ + struct dw100_ctx *ctx = + container_of(ctrl->handler, struct dw100_ctx, hdl); + + u32 sw, sh, mw, mh, idx; + u16 qx, qy, qdx, qdy, qsh, qsw; + u32 *map = ctrl->p_cur.p_u32; + + sw = ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width; + sh = ctx->q_data[DW100_QUEUE_SRC].pix_fmt.height; + + mw = ctrl->dims[0]; + mh = ctrl->dims[1]; + + qsw = dw100_map_convert_to_uq12_4(sw); + qsh = dw100_map_convert_to_uq12_4(sh); + qdx = qsw / (mw - 1); + qdy = qsh / (mh - 1); + + ctx->map_width = mw; + ctx->map_height = mh; + ctx->map_size = mh * mw * sizeof(u32); + + for (idx = from_idx; idx < elems; idx++) { + qy = min_t(u32, (idx / mw) * qdy, qsh); + qx = min_t(u32, (idx % mw) * qdx, qsw); + map[idx] = dw100_map_format_coordinates(qx, qy); + } + + ctx->user_map_is_set = false; +} + +static const struct v4l2_ctrl_type_ops dw100_ctrl_type_ops = { + .init = dw100_ctrl_dewarping_map_init, + .validate = v4l2_ctrl_type_op_validate, + .log = v4l2_ctrl_type_op_log, + .equal = v4l2_ctrl_type_op_equal, +}; + +static const struct v4l2_ctrl_config controls[] = { + [DW100_CTRL_DEWARPING_MAP] = { + .ops = &dw100_ctrl_ops, + .type_ops = &dw100_ctrl_type_ops, + .id = V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP, + .name = "Dewarping Vertex Map", + .type = V4L2_CTRL_TYPE_U32, + .min = 0x00000000, + .max = 0xffffffff, + .step = 1, + .def = 0, + .dims = { DW100_DEF_LUT_W, DW100_DEF_LUT_H }, + }, +}; + +static int dw100_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct dw100_ctx *ctx = vb2_get_drv_priv(vq); + const struct v4l2_pix_format_mplane *format; + unsigned int i; + + format = &dw100_get_q_data(ctx, vq->type)->pix_fmt; + + if (*nplanes) { + if (*nplanes != format->num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; ++i) { + if (sizes[i] < format->plane_fmt[i].sizeimage) + return -EINVAL; + } + + return 0; + } + + *nplanes = format->num_planes; + + for (i = 0; i < format->num_planes; ++i) + sizes[i] = format->plane_fmt[i].sizeimage; + + return 0; +} + +static int dw100_buf_prepare(struct vb2_buffer *vb) +{ + unsigned int i; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct dw100_device *dw_dev = ctx->dw_dev; + const struct v4l2_pix_format_mplane *pix_fmt = + &dw100_get_q_data(ctx, vb->vb2_queue->type)->pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field != V4L2_FIELD_NONE) { + dev_dbg(&dw_dev->pdev->dev, "%x field isn't supported\n", + vbuf->field); + return -EINVAL; + } + } + + for (i = 0; i < pix_fmt->num_planes; i++) { + unsigned long size = pix_fmt->plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < size) { + dev_dbg(&dw_dev->pdev->dev, + "User buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void dw100_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void dw100_return_all_buffers(struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct dw100_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + return; + v4l2_m2m_buf_done(vbuf, state); + } +} + +static int dw100_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct dw100_ctx *ctx = vb2_get_drv_priv(q); + struct dw100_q_data *q_data = dw100_get_q_data(ctx, q->type); + int ret; + + q_data->sequence = 0; + + ret = dw100_create_mapping(ctx); + if (ret) + goto err; + + ret = pm_runtime_resume_and_get(&ctx->dw_dev->pdev->dev); + if (ret) { + dw100_destroy_mapping(ctx); + goto err; + } + + return 0; +err: + dw100_return_all_buffers(q, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void dw100_stop_streaming(struct vb2_queue *q) +{ + struct dw100_ctx *ctx = vb2_get_drv_priv(q); + + dw100_return_all_buffers(q, VB2_BUF_STATE_ERROR); + + pm_runtime_put_sync(&ctx->dw_dev->pdev->dev); + + dw100_destroy_mapping(ctx); +} + +static const struct vb2_ops dw100_qops = { + .queue_setup = dw100_queue_setup, + .buf_prepare = dw100_buf_prepare, + .buf_queue = dw100_buf_queue, + .start_streaming = dw100_start_streaming, + .stop_streaming = dw100_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct dw100_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &dw100_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->vq_mutex; + src_vq->dev = ctx->dw_dev->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &dw100_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->vq_mutex; + dst_vq->dev = ctx->dw_dev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static int dw100_open(struct file *file) +{ + struct dw100_device *dw_dev = video_drvdata(file); + struct dw100_ctx *ctx; + struct v4l2_ctrl_handler *hdl; + struct v4l2_pix_format_mplane *pix_fmt; + int ret, i; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_init(&ctx->vq_mutex); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dw_dev = dw_dev; + + ctx->q_data[DW100_QUEUE_SRC].fmt = &formats[0]; + + pix_fmt = &ctx->q_data[DW100_QUEUE_SRC].pix_fmt; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_REC709; + pix_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_fmt->colorspace); + pix_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_fmt->colorspace); + pix_fmt->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, pix_fmt->colorspace, + pix_fmt->ycbcr_enc); + + v4l2_fill_pixfmt_mp(pix_fmt, formats[0].fourcc, DW100_DEF_W, DW100_DEF_H); + + ctx->q_data[DW100_QUEUE_SRC].crop.top = 0; + ctx->q_data[DW100_QUEUE_SRC].crop.left = 0; + ctx->q_data[DW100_QUEUE_SRC].crop.width = DW100_DEF_W; + ctx->q_data[DW100_QUEUE_SRC].crop.height = DW100_DEF_H; + + ctx->q_data[DW100_QUEUE_DST] = ctx->q_data[DW100_QUEUE_SRC]; + + hdl = &ctx->hdl; + v4l2_ctrl_handler_init(hdl, ARRAY_SIZE(controls)); + for (i = 0; i < ARRAY_SIZE(controls); i++) { + ctx->ctrls[i] = v4l2_ctrl_new_custom(hdl, &controls[i], NULL); + if (hdl->error) { + dev_err(&ctx->dw_dev->pdev->dev, + "Adding control (%d) failed\n", i); + ret = hdl->error; + goto err; + } + } + ctx->fh.ctrl_handler = hdl; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dw_dev->m2m_dev, + ctx, &dw100_m2m_queue_init); + + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err; + } + + v4l2_fh_add(&ctx->fh); + + return 0; + +err: + v4l2_ctrl_handler_free(hdl); + v4l2_fh_exit(&ctx->fh); + mutex_destroy(&ctx->vq_mutex); + kfree(ctx); + + return ret; +} + +static int dw100_release(struct file *file) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->hdl); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + mutex_destroy(&ctx->vq_mutex); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations dw100_fops = { + .owner = THIS_MODULE, + .open = dw100_open, + .release = dw100_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int dw100_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, "DW100 dewarper", sizeof(cap->card)); + + return 0; +} + +static int dw100_enum_fmt_vid(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int i, num = 0; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].types & to_dw100_fmt_type(f->type)) { + if (num == f->index) { + f->pixelformat = formats[i].fourcc; + return 0; + } + ++num; + } + } + + return -EINVAL; +} + +static int dw100_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + const struct dw100_fmt *fmt; + + if (fsize->index) + return -EINVAL; + + fmt = dw100_find_pixel_format(fsize->pixel_format, + DW100_FMT_OUTPUT | DW100_FMT_CAPTURE); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = dw100_frmsize_stepwise; + + return 0; +} + +static int dw100_g_fmt_vid(struct file *file, void *priv, struct v4l2_format *f) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + struct vb2_queue *vq; + struct dw100_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = dw100_get_q_data(ctx, f->type); + + f->fmt.pix_mp = q_data->pix_fmt; + + return 0; +} + +static int dw100_try_fmt(struct file *file, struct v4l2_format *f) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + const struct dw100_fmt *fmt; + + fmt = dw100_find_format(f); + if (!fmt) { + fmt = &formats[0]; + pix->pixelformat = fmt->fourcc; + } + + v4l2_apply_frmsize_constraints(&pix->width, &pix->height, + &dw100_frmsize_stepwise); + + v4l2_fill_pixfmt_mp(pix, fmt->fourcc, pix->width, pix->height); + + pix->field = V4L2_FIELD_NONE; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) + pix->colorspace = V4L2_COLORSPACE_REC709; + if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT) + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) + pix->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, + pix->colorspace, + pix->ycbcr_enc); + } else { + /* + * The DW100 can't perform colorspace conversion, the colorspace + * on the capture queue must be identical to the output queue. + */ + const struct dw100_q_data *q_data = + dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + pix->colorspace = q_data->pix_fmt.colorspace; + pix->xfer_func = q_data->pix_fmt.xfer_func; + pix->ycbcr_enc = q_data->pix_fmt.ycbcr_enc; + pix->quantization = q_data->pix_fmt.quantization; + } + + return 0; +} + +static int dw100_s_fmt(struct dw100_ctx *ctx, struct v4l2_format *f) +{ + struct dw100_q_data *q_data; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = dw100_get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + dev_dbg(&ctx->dw_dev->pdev->dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + q_data->fmt = dw100_find_format(f); + q_data->pix_fmt = f->fmt.pix_mp; + q_data->crop.top = 0; + q_data->crop.left = 0; + q_data->crop.width = f->fmt.pix_mp.width; + q_data->crop.height = f->fmt.pix_mp.height; + + /* Propagate buffers encoding */ + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct dw100_q_data *dst_q_data = + dw100_get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + dst_q_data->pix_fmt.colorspace = q_data->pix_fmt.colorspace; + dst_q_data->pix_fmt.ycbcr_enc = q_data->pix_fmt.ycbcr_enc; + dst_q_data->pix_fmt.quantization = q_data->pix_fmt.quantization; + dst_q_data->pix_fmt.xfer_func = q_data->pix_fmt.xfer_func; + } + + dev_dbg(&ctx->dw_dev->pdev->dev, + "Setting format for type %u, wxh: %ux%u, fmt: %p4cc\n", + f->type, q_data->pix_fmt.width, q_data->pix_fmt.height, + &q_data->pix_fmt.pixelformat); + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + int ret; + u32 dims[V4L2_CTRL_MAX_DIMS] = {}; + struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP]; + + dims[0] = dw100_get_n_vertices_from_length(q_data->pix_fmt.width); + dims[1] = dw100_get_n_vertices_from_length(q_data->pix_fmt.height); + + ret = v4l2_ctrl_modify_dimensions(ctrl, dims); + + if (ret) { + dev_err(&ctx->dw_dev->pdev->dev, + "Modifying LUT dimensions failed with error %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int dw100_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + return dw100_try_fmt(file, f); +} + +static int dw100_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + int ret; + + ret = dw100_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + ret = dw100_s_fmt(ctx, f); + if (ret) + return ret; + + return 0; +} + +static int dw100_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + return dw100_try_fmt(file, f); +} + +static int dw100_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + int ret; + + ret = dw100_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = dw100_s_fmt(ctx, f); + if (ret) + return ret; + + return 0; +} + +static int dw100_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + struct dw100_q_data *src_q_data; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = src_q_data->pix_fmt.width; + sel->r.height = src_q_data->pix_fmt.height; + break; + case V4L2_SEL_TGT_CROP: + sel->r.top = src_q_data->crop.top; + sel->r.left = src_q_data->crop.left; + sel->r.width = src_q_data->crop.width; + sel->r.height = src_q_data->crop.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dw100_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct dw100_ctx *ctx = dw100_file2ctx(file); + struct dw100_q_data *src_q_data; + u32 qscalex, qscaley, qscale; + int x, y, w, h; + unsigned int wframe, hframe; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + dev_dbg(&ctx->dw_dev->pdev->dev, + ">>> Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", + sel->type, sel->target, + sel->r.width, sel->r.height, sel->r.left, sel->r.top); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + wframe = src_q_data->pix_fmt.width; + hframe = src_q_data->pix_fmt.height; + + sel->r.top = clamp_t(int, sel->r.top, 0, hframe - DW100_MIN_H); + sel->r.left = clamp_t(int, sel->r.left, 0, wframe - DW100_MIN_W); + sel->r.height = + clamp(sel->r.height, DW100_MIN_H, hframe - sel->r.top); + sel->r.width = + clamp(sel->r.width, DW100_MIN_W, wframe - sel->r.left); + + /* UQ16.16 for float operations */ + qscalex = (sel->r.width << 16) / wframe; + qscaley = (sel->r.height << 16) / hframe; + y = sel->r.top; + x = sel->r.left; + if (qscalex == qscaley) { + qscale = qscalex; + } else { + switch (sel->flags) { + case 0: + qscale = (qscalex + qscaley) / 2; + break; + case V4L2_SEL_FLAG_GE: + qscale = max(qscaley, qscalex); + break; + case V4L2_SEL_FLAG_LE: + qscale = min(qscaley, qscalex); + break; + case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE: + return -ERANGE; + default: + return -EINVAL; + } + } + + w = (u32)((((u64)wframe << 16) * qscale) >> 32); + h = (u32)((((u64)hframe << 16) * qscale) >> 32); + x = x + (sel->r.width - w) / 2; + y = y + (sel->r.height - h) / 2; + x = min(wframe - w, (unsigned int)max(0, x)); + y = min(hframe - h, (unsigned int)max(0, y)); + + sel->r.top = y; + sel->r.left = x; + sel->r.width = w; + sel->r.height = h; + + src_q_data->crop.top = sel->r.top; + src_q_data->crop.left = sel->r.left; + src_q_data->crop.width = sel->r.width; + src_q_data->crop.height = sel->r.height; + break; + + default: + return -EINVAL; + } + + dev_dbg(&ctx->dw_dev->pdev->dev, + "<<< Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", + sel->type, sel->target, + sel->r.width, sel->r.height, sel->r.left, sel->r.top); + + return 0; +} + +static const struct v4l2_ioctl_ops dw100_ioctl_ops = { + .vidioc_querycap = dw100_querycap, + + .vidioc_enum_fmt_vid_cap = dw100_enum_fmt_vid, + .vidioc_enum_framesizes = dw100_enum_framesizes, + .vidioc_g_fmt_vid_cap_mplane = dw100_g_fmt_vid, + .vidioc_try_fmt_vid_cap_mplane = dw100_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = dw100_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = dw100_enum_fmt_vid, + .vidioc_g_fmt_vid_out_mplane = dw100_g_fmt_vid, + .vidioc_try_fmt_vid_out_mplane = dw100_try_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane = dw100_s_fmt_vid_out, + + .vidioc_g_selection = dw100_g_selection, + .vidioc_s_selection = dw100_s_selection, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void dw100_job_finish(struct dw100_device *dw_dev, bool with_error) +{ + struct dw100_ctx *curr_ctx; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + enum vb2_buffer_state buf_state; + + curr_ctx = v4l2_m2m_get_curr_priv(dw_dev->m2m_dev); + + if (!curr_ctx) { + dev_err(&dw_dev->pdev->dev, + "Instance released before the end of transaction\n"); + return; + } + + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + + if (likely(!with_error)) + buf_state = VB2_BUF_STATE_DONE; + else + buf_state = VB2_BUF_STATE_ERROR; + + v4l2_m2m_buf_done(src_vb, buf_state); + v4l2_m2m_buf_done(dst_vb, buf_state); + + dev_dbg(&dw_dev->pdev->dev, "Finishing transaction with%s error(s)\n", + with_error ? "" : "out"); + + v4l2_m2m_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx); +} + +static void dw100_hw_reset(struct dw100_device *dw_dev) +{ + u32 val; + + val = dw100_read(dw_dev, DW100_DEWARP_CTRL); + val |= DW100_DEWARP_CTRL_ENABLE; + val |= DW100_DEWARP_CTRL_SOFT_RESET; + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); + val &= ~DW100_DEWARP_CTRL_SOFT_RESET; + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); +} + +static void _dw100_hw_set_master_bus_enable(struct dw100_device *dw_dev, + unsigned int enable) +{ + u32 val; + + dev_dbg(&dw_dev->pdev->dev, "%sable master bus\n", + enable ? "En" : "Dis"); + + val = dw100_read(dw_dev, DW100_BUS_CTRL); + + if (enable) + val |= DW100_BUS_CTRL_AXI_MASTER_ENABLE; + else + val &= ~DW100_BUS_CTRL_AXI_MASTER_ENABLE; + + dw100_write(dw_dev, DW100_BUS_CTRL, val); +} + +static void dw100_hw_master_bus_enable(struct dw100_device *dw_dev) +{ + _dw100_hw_set_master_bus_enable(dw_dev, 1); +} + +static void dw100_hw_master_bus_disable(struct dw100_device *dw_dev) +{ + _dw100_hw_set_master_bus_enable(dw_dev, 0); +} + +static void dw100_hw_dewarp_start(struct dw100_device *dw_dev) +{ + u32 val; + + val = dw100_read(dw_dev, DW100_DEWARP_CTRL); + + dev_dbg(&dw_dev->pdev->dev, "Starting Hardware CTRL:0x%08x\n", val); + dw100_write(dw_dev, DW100_DEWARP_CTRL, val | DW100_DEWARP_CTRL_START); + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); +} + +static void dw100_hw_init_ctrl(struct dw100_device *dw_dev) +{ + u32 val; + /* + * Input format YUV422_SP + * Output format YUV422_SP + * No hardware handshake (SW) + * No automatic double src buffering (Single) + * No automatic double dst buffering (Single) + * No Black Line + * Prefetch image pixel traversal + */ + + val = DW100_DEWARP_CTRL_ENABLE + /* Valid only for auto prefetch mode*/ + | DW100_DEWARP_CTRL_PREFETCH_THRESHOLD(32); + + /* + * Calculation mode required to support any scaling factor, + * but x4 slower than traversal mode. + * + * DW100_DEWARP_CTRL_PREFETCH_MODE_TRAVERSAL + * DW100_DEWARP_CTRL_PREFETCH_MODE_CALCULATION + * DW100_DEWARP_CTRL_PREFETCH_MODE_AUTO + * + * TODO: Find heuristics requiring calculation mode + */ + val |= DW100_DEWARP_CTRL_PREFETCH_MODE_CALCULATION; + + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); +} + +static void dw100_hw_set_pixel_boundary(struct dw100_device *dw_dev) +{ + u32 val; + + val = DW100_BOUNDARY_PIXEL_V(128) + | DW100_BOUNDARY_PIXEL_U(128) + | DW100_BOUNDARY_PIXEL_Y(0); + + dw100_write(dw_dev, DW100_BOUNDARY_PIXEL, val); +} + +static void dw100_hw_set_scale(struct dw100_device *dw_dev, u8 scale) +{ + dev_dbg(&dw_dev->pdev->dev, "Setting scale factor to %u\n", scale); + + dw100_write(dw_dev, DW100_SCALE_FACTOR, scale); +} + +static void dw100_hw_set_roi(struct dw100_device *dw_dev, u32 x, u32 y) +{ + u32 val; + + dev_dbg(&dw_dev->pdev->dev, "Setting ROI region to %u.%u\n", x, y); + + val = DW100_ROI_START_X(x) | DW100_ROI_START_Y(y); + + dw100_write(dw_dev, DW100_ROI_START, val); +} + +static void dw100_hw_set_src_crop(struct dw100_device *dw_dev, + const struct dw100_q_data *src_q_data, + const struct dw100_q_data *dst_q_data) +{ + const struct v4l2_rect *rect = &src_q_data->crop; + u32 src_scale, qscale, left_scale, top_scale; + + /* HW Scale is UQ1.7 encoded */ + src_scale = (rect->width << 7) / src_q_data->pix_fmt.width; + dw100_hw_set_scale(dw_dev, src_scale); + + qscale = (dst_q_data->pix_fmt.width << 7) / src_q_data->pix_fmt.width; + + left_scale = ((rect->left << 7) * qscale) >> 14; + top_scale = ((rect->top << 7) * qscale) >> 14; + + dw100_hw_set_roi(dw_dev, left_scale, top_scale); +} + +static void dw100_hw_set_source(struct dw100_device *dw_dev, + const struct dw100_q_data *q_data, + struct vb2_buffer *buffer) +{ + u32 width, height, stride, fourcc, val; + const struct dw100_fmt *fmt = q_data->fmt; + dma_addr_t addr_y = vb2_dma_contig_plane_dma_addr(buffer, 0); + dma_addr_t addr_uv; + + width = q_data->pix_fmt.width; + height = q_data->pix_fmt.height; + stride = q_data->pix_fmt.plane_fmt[0].bytesperline; + fourcc = q_data->fmt->fourcc; + + if (q_data->pix_fmt.num_planes == 2) + addr_uv = vb2_dma_contig_plane_dma_addr(buffer, 1); + else + addr_uv = addr_y + (stride * height); + + dev_dbg(&dw_dev->pdev->dev, + "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", + width, height, stride, &fourcc, &addr_y); + + /* Pixel Format */ + val = dw100_read(dw_dev, DW100_DEWARP_CTRL); + + val &= ~DW100_DEWARP_CTRL_INPUT_FORMAT_MASK; + val |= DW100_DEWARP_CTRL_INPUT_FORMAT(fmt->reg_format); + + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); + + /* Swap */ + val = dw100_read(dw_dev, DW100_SWAP_CONTROL); + + val &= ~DW100_SWAP_CONTROL_SRC_MASK; + /* + * Data swapping is performed only on Y plane for source image. + */ + if (fmt->reg_swap_uv && + fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED) + val |= DW100_SWAP_CONTROL_SRC(DW100_SWAP_CONTROL_Y + (DW100_SWAP_CONTROL_BYTE)); + + dw100_write(dw_dev, DW100_SWAP_CONTROL, val); + + /* Image resolution */ + dw100_write(dw_dev, DW100_SRC_IMG_SIZE, + DW100_IMG_SIZE_WIDTH(width) | DW100_IMG_SIZE_HEIGHT(height)); + + dw100_write(dw_dev, DW100_SRC_IMG_STRIDE, stride); + + /* Buffers */ + dw100_write(dw_dev, DW100_SRC_IMG_Y_BASE, DW100_IMG_Y_BASE(addr_y)); + dw100_write(dw_dev, DW100_SRC_IMG_UV_BASE, DW100_IMG_UV_BASE(addr_uv)); +} + +static void dw100_hw_set_destination(struct dw100_device *dw_dev, + const struct dw100_q_data *q_data, + const struct dw100_fmt *ifmt, + struct vb2_buffer *buffer) +{ + u32 width, height, stride, fourcc, val, size_y, size_uv; + const struct dw100_fmt *fmt = q_data->fmt; + dma_addr_t addr_y, addr_uv; + + width = q_data->pix_fmt.width; + height = q_data->pix_fmt.height; + stride = q_data->pix_fmt.plane_fmt[0].bytesperline; + fourcc = fmt->fourcc; + + addr_y = vb2_dma_contig_plane_dma_addr(buffer, 0); + size_y = q_data->pix_fmt.plane_fmt[0].sizeimage; + + if (q_data->pix_fmt.num_planes == 2) { + addr_uv = vb2_dma_contig_plane_dma_addr(buffer, 1); + size_uv = q_data->pix_fmt.plane_fmt[1].sizeimage; + } else { + addr_uv = addr_y + ALIGN(stride * height, 16); + size_uv = size_y; + if (fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV420_SP) + size_uv /= 2; + } + + dev_dbg(&dw_dev->pdev->dev, + "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", + width, height, stride, &fourcc, &addr_y); + + /* Pixel Format */ + val = dw100_read(dw_dev, DW100_DEWARP_CTRL); + + val &= ~DW100_DEWARP_CTRL_OUTPUT_FORMAT_MASK; + val |= DW100_DEWARP_CTRL_OUTPUT_FORMAT(fmt->reg_format); + + dw100_write(dw_dev, DW100_DEWARP_CTRL, val); + + /* Swap */ + val = dw100_read(dw_dev, DW100_SWAP_CONTROL); + + val &= ~DW100_SWAP_CONTROL_DST_MASK; + + /* + * Avoid to swap twice + */ + if (fmt->reg_swap_uv ^ + (ifmt->reg_swap_uv && ifmt->reg_format != + DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED)) { + if (fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED) + val |= DW100_SWAP_CONTROL_DST(DW100_SWAP_CONTROL_Y + (DW100_SWAP_CONTROL_BYTE)); + else + val |= DW100_SWAP_CONTROL_DST(DW100_SWAP_CONTROL_UV + (DW100_SWAP_CONTROL_BYTE)); + } + + dw100_write(dw_dev, DW100_SWAP_CONTROL, val); + + /* Image resolution */ + dw100_write(dw_dev, DW100_DST_IMG_SIZE, + DW100_IMG_SIZE_WIDTH(width) | DW100_IMG_SIZE_HEIGHT(height)); + dw100_write(dw_dev, DW100_DST_IMG_STRIDE, stride); + dw100_write(dw_dev, DW100_DST_IMG_Y_BASE, DW100_IMG_Y_BASE(addr_y)); + dw100_write(dw_dev, DW100_DST_IMG_UV_BASE, DW100_IMG_UV_BASE(addr_uv)); + dw100_write(dw_dev, DW100_DST_IMG_Y_SIZE1, DW100_DST_IMG_Y_SIZE(size_y)); + dw100_write(dw_dev, DW100_DST_IMG_UV_SIZE1, + DW100_DST_IMG_UV_SIZE(size_uv)); +} + +static void dw100_hw_set_mapping(struct dw100_device *dw_dev, dma_addr_t addr, + u32 width, u32 height) +{ + dev_dbg(&dw_dev->pdev->dev, + "Set HW mapping registers for %ux%u addr:%pad", + width, height, &addr); + + dw100_write(dw_dev, DW100_MAP_LUT_ADDR, DW100_MAP_LUT_ADDR_ADDR(addr)); + dw100_write(dw_dev, DW100_MAP_LUT_SIZE, DW100_MAP_LUT_SIZE_WIDTH(width) + | DW100_MAP_LUT_SIZE_HEIGHT(height)); +} + +static void dw100_hw_clear_irq(struct dw100_device *dw_dev, unsigned int irq) +{ + dw100_write(dw_dev, DW100_INTERRUPT_STATUS, + DW100_INTERRUPT_STATUS_INT_CLEAR(irq)); +} + +static void dw100_hw_enable_irq(struct dw100_device *dw_dev) +{ + dw100_write(dw_dev, DW100_INTERRUPT_STATUS, + DW100_INTERRUPT_STATUS_INT_ENABLE_MASK); +} + +static void dw100_hw_disable_irq(struct dw100_device *dw_dev) +{ + dw100_write(dw_dev, DW100_INTERRUPT_STATUS, 0); +} + +static u32 dw_hw_get_pending_irqs(struct dw100_device *dw_dev) +{ + u32 val; + + val = dw100_read(dw_dev, DW100_INTERRUPT_STATUS); + + return DW100_INTERRUPT_STATUS_INT_STATUS(val); +} + +static irqreturn_t dw100_irq_handler(int irq, void *dev_id) +{ + struct dw100_device *dw_dev = dev_id; + u32 pending_irqs, err_irqs, frame_done_irq; + bool with_error = true; + + pending_irqs = dw_hw_get_pending_irqs(dw_dev); + frame_done_irq = pending_irqs & DW100_INTERRUPT_STATUS_INT_FRAME_DONE; + err_irqs = DW100_INTERRUPT_STATUS_INT_ERR_STATUS(pending_irqs); + + if (frame_done_irq) { + dev_dbg(&dw_dev->pdev->dev, "Frame done interrupt\n"); + with_error = false; + err_irqs &= ~DW100_INTERRUPT_STATUS_INT_ERR_STATUS + (DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE); + } + + if (err_irqs) + dev_err(&dw_dev->pdev->dev, "Interrupt error: %#x\n", err_irqs); + + dw100_hw_disable_irq(dw_dev); + dw100_hw_master_bus_disable(dw_dev); + dw100_hw_clear_irq(dw_dev, pending_irqs | + DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT); + + dw100_job_finish(dw_dev, with_error); + + return IRQ_HANDLED; +} + +static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, + struct vb2_v4l2_buffer *out_vb) +{ + struct dw100_device *dw_dev = ctx->dw_dev; + + out_vb->sequence = + dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++; + in_vb->sequence = + dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)->sequence++; + + dev_dbg(&ctx->dw_dev->pdev->dev, + "Starting queues %p->%p, sequence %u->%u\n", + v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE), + v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), + in_vb->sequence, out_vb->sequence); + + v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); + + /* Now, let's deal with hardware ... */ + dw100_hw_master_bus_disable(dw_dev); + dw100_hw_init_ctrl(dw_dev); + dw100_hw_set_pixel_boundary(dw_dev); + dw100_hw_set_src_crop(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], + &ctx->q_data[DW100_QUEUE_DST]); + dw100_hw_set_source(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], + &in_vb->vb2_buf); + dw100_hw_set_destination(dw_dev, &ctx->q_data[DW100_QUEUE_DST], + ctx->q_data[DW100_QUEUE_SRC].fmt, + &out_vb->vb2_buf); + dw100_hw_set_mapping(dw_dev, ctx->map_dma, + ctx->map_width, ctx->map_height); + dw100_hw_enable_irq(dw_dev); + dw100_hw_dewarp_start(dw_dev); + + /* Enable Bus */ + dw100_hw_master_bus_enable(dw_dev); +} + +static void dw100_device_run(void *priv) +{ + struct dw100_ctx *ctx = priv; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + dw100_start(ctx, src_buf, dst_buf); +} + +static const struct v4l2_m2m_ops dw100_m2m_ops = { + .device_run = dw100_device_run, +}; + +static struct video_device *dw100_init_video_device(struct dw100_device *dw_dev) +{ + struct video_device *vfd = &dw_dev->vfd; + + vfd->vfl_dir = VFL_DIR_M2M; + vfd->fops = &dw100_fops; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + vfd->ioctl_ops = &dw100_ioctl_ops; + vfd->minor = -1; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dw_dev->v4l2_dev; + vfd->lock = &dw_dev->vfd_mutex; + + strscpy(vfd->name, DRV_NAME, sizeof(vfd->name)); + mutex_init(vfd->lock); + video_set_drvdata(vfd, dw_dev); + + return vfd; +} + +static int dw100_dump_regs_show(struct seq_file *m, void *private) +{ + struct dw100_device *dw_dev = m->private; + int ret; + + ret = pm_runtime_resume_and_get(&dw_dev->pdev->dev); + if (ret < 0) + return ret; + + ret = dw100_dump_regs(m); + + pm_runtime_put_sync(&dw_dev->pdev->dev); + + return ret; +} +DEFINE_SHOW_ATTRIBUTE(dw100_dump_regs); + +static void dw100_debugfs_init(struct dw100_device *dw_dev) +{ + dw_dev->debugfs_root = + debugfs_create_dir(dev_name(&dw_dev->pdev->dev), NULL); + + debugfs_create_file("dump_regs", 0600, dw_dev->debugfs_root, dw_dev, + &dw100_dump_regs_fops); +} + +static void dw100_debugfs_exit(struct dw100_device *dw_dev) +{ + debugfs_remove_recursive(dw_dev->debugfs_root); +} + +static int dw100_probe(struct platform_device *pdev) +{ + struct dw100_device *dw_dev; + struct video_device *vfd; + struct resource *res; + int ret, irq; + + dw_dev = devm_kzalloc(&pdev->dev, sizeof(*dw_dev), GFP_KERNEL); + if (!dw_dev) + return -ENOMEM; + dw_dev->pdev = pdev; + + ret = devm_clk_bulk_get_all(&pdev->dev, &dw_dev->clks); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to get clocks: %d\n", ret); + return ret; + } + dw_dev->num_clks = ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dw_dev->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dw_dev->mmio)) + return PTR_ERR(dw_dev->mmio); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + platform_set_drvdata(pdev, dw_dev); + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to resume the device: %d\n", ret); + goto err_pm; + } + + pm_runtime_put_sync(&pdev->dev); + + ret = devm_request_irq(&pdev->dev, irq, dw100_irq_handler, IRQF_ONESHOT, + dev_name(&pdev->dev), dw_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &dw_dev->v4l2_dev); + if (ret) + goto err_pm; + + vfd = dw100_init_video_device(dw_dev); + + dw_dev->m2m_dev = v4l2_m2m_init(&dw100_m2m_ops); + if (IS_ERR(dw_dev->m2m_dev)) { + dev_err(&pdev->dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(dw_dev->m2m_dev); + goto err_v4l2; + } + + dw_dev->mdev.dev = &pdev->dev; + strscpy(dw_dev->mdev.model, "dw100", sizeof(dw_dev->mdev.model)); + media_device_init(&dw_dev->mdev); + dw_dev->v4l2_dev.mdev = &dw_dev->mdev; + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(&pdev->dev, "Failed to register video device\n"); + goto err_m2m; + } + + ret = v4l2_m2m_register_media_controller(dw_dev->m2m_dev, vfd, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + if (ret) { + dev_err(&pdev->dev, "Failed to init mem2mem media controller\n"); + goto error_v4l2; + } + + ret = media_device_register(&dw_dev->mdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register mem2mem media device\n"); + goto error_m2m_mc; + } + + dw100_debugfs_init(dw_dev); + + dev_info(&pdev->dev, + "dw100 v4l2 m2m registered as /dev/video%u\n", vfd->num); + + return 0; + +error_m2m_mc: + v4l2_m2m_unregister_media_controller(dw_dev->m2m_dev); +error_v4l2: + video_unregister_device(vfd); +err_m2m: + v4l2_m2m_release(dw_dev->m2m_dev); +err_v4l2: + v4l2_device_unregister(&dw_dev->v4l2_dev); +err_pm: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int dw100_remove(struct platform_device *pdev) +{ + struct dw100_device *dw_dev = platform_get_drvdata(pdev); + + dw100_debugfs_exit(dw_dev); + + pm_runtime_disable(&pdev->dev); + + media_device_unregister(&dw_dev->mdev); + v4l2_m2m_unregister_media_controller(dw_dev->m2m_dev); + media_device_cleanup(&dw_dev->mdev); + + video_unregister_device(&dw_dev->vfd); + mutex_destroy(dw_dev->vfd.lock); + v4l2_m2m_release(dw_dev->m2m_dev); + v4l2_device_unregister(&dw_dev->v4l2_dev); + + return 0; +} + +static int __maybe_unused dw100_runtime_suspend(struct device *dev) +{ + struct dw100_device *dw_dev = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(dw_dev->num_clks, dw_dev->clks); + + return 0; +} + +static int __maybe_unused dw100_runtime_resume(struct device *dev) +{ + int ret; + struct dw100_device *dw_dev = dev_get_drvdata(dev); + + ret = clk_bulk_prepare_enable(dw_dev->num_clks, dw_dev->clks); + + if (ret) + return ret; + + dw100_hw_reset(dw_dev); + + return 0; +} + +static const struct dev_pm_ops dw100_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(dw100_runtime_suspend, + dw100_runtime_resume, NULL) +}; + +static const struct of_device_id dw100_dt_ids[] = { + { .compatible = "nxp,imx8mp-dw100", .data = NULL }, + { }, +}; +MODULE_DEVICE_TABLE(of, dw100_dt_ids); + +static struct platform_driver dw100_driver = { + .probe = dw100_probe, + .remove = dw100_remove, + .driver = { + .name = DRV_NAME, + .pm = &dw100_pm, + .of_match_table = dw100_dt_ids, + }, +}; + +module_platform_driver(dw100_driver); + +MODULE_DESCRIPTION("DW100 Hardware dewarper"); +MODULE_AUTHOR("Xavier Roumegue "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/nxp/dw100/dw100_regs.h b/drivers/media/platform/nxp/dw100/dw100_regs.h new file mode 100644 index 000000000000..e85dfeff9056 --- /dev/null +++ b/drivers/media/platform/nxp/dw100/dw100_regs.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DW100 Hardware dewarper + * + * Copyright 2022 NXP + * Author: Xavier Roumegue (xavier.roumegue@oss.nxp.com) + */ + +#ifndef _DW100_REGS_H_ +#define _DW100_REGS_H_ + +/* AHB register offset */ +#define DW100_DEWARP_ID 0x00 +#define DW100_DEWARP_CTRL 0x04 +#define DW100_DEWARP_CTRL_ENABLE BIT(0) +#define DW100_DEWARP_CTRL_START BIT(1) +#define DW100_DEWARP_CTRL_SOFT_RESET BIT(2) +#define DW100_DEWARP_CTRL_FORMAT_YUV422_SP 0UL +#define DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED 1UL +#define DW100_DEWARP_CTRL_FORMAT_YUV420_SP 2UL +#define DW100_DEWARP_CTRL_INPUT_FORMAT_MASK GENMASK(5, 4) +#define DW100_DEWARP_CTRL_INPUT_FORMAT(x) ((x) << 4) +#define DW100_DEWARP_CTRL_OUTPUT_FORMAT(x) ((x) << 6) +#define DW100_DEWARP_CTRL_OUTPUT_FORMAT_MASK GENMASK(7, 6) +#define DW100_DEWARP_CTRL_SRC_AUTO_SHADOW BIT(8) +#define DW100_DEWARP_CTRL_HW_HANDSHAKE BIT(9) +#define DW100_DEWARP_CTRL_DST_AUTO_SHADOW BIT(10) +#define DW100_DEWARP_CTRL_SPLIT_LINE BIT(11) +#define DW100_DEWARP_CTRL_PREFETCH_MODE_MASK GENMASK(17, 16) +#define DW100_DEWARP_CTRL_PREFETCH_MODE_TRAVERSAL (0UL << 16) +#define DW100_DEWARP_CTRL_PREFETCH_MODE_CALCULATION (1UL << 16) +#define DW100_DEWARP_CTRL_PREFETCH_MODE_AUTO (2UL << 16) +#define DW100_DEWARP_CTRL_PREFETCH_THRESHOLD_MASK GENMASK(24, 18) +#define DW100_DEWARP_CTRL_PREFETCH_THRESHOLD(x) ((x) << 18) + +#define DW100_MAP_LUT_ADDR 0x08 +#define DW100_MAP_LUT_ADDR_ADDR(addr) (((addr) >> 4) & GENMASK(29, 0)) +#define DW100_MAP_LUT_SIZE 0x0c +#define DW100_MAP_LUT_SIZE_WIDTH(w) (((w) & GENMASK(10, 0)) << 0) +#define DW100_MAP_LUT_SIZE_HEIGHT(h) (((h) & GENMASK(10, 0)) << 16) +#define DW100_SRC_IMG_Y_BASE 0x10 +#define DW100_IMG_Y_BASE(base) (((base) >> 4) & GENMASK(29, 0)) +#define DW100_SRC_IMG_UV_BASE 0x14 +#define DW100_IMG_UV_BASE(base) (((base) >> 4) & GENMASK(29, 0)) +#define DW100_SRC_IMG_SIZE 0x18 +#define DW100_IMG_SIZE_WIDTH(w) (((w) & GENMASK(12, 0)) << 0) +#define DW100_IMG_SIZE_HEIGHT(h) (((h) & GENMASK(12, 0)) << 16) + +#define DW100_SRC_IMG_STRIDE 0x1c +#define DW100_MAP_LUT_ADDR2 0x20 +#define DW100_MAP_LUT_SIZE2 0x24 +#define DW100_SRC_IMG_Y_BASE2 0x28 +#define DW100_SRC_IMG_UV_BASE2 0x2c +#define DW100_SRC_IMG_SIZE2 0x30 +#define DW100_SRC_IMG_STRIDE2 0x34 +#define DW100_DST_IMG_Y_BASE 0x38 +#define DW100_DST_IMG_UV_BASE 0x3c +#define DW100_DST_IMG_SIZE 0x40 +#define DW100_DST_IMG_STRIDE 0x44 +#define DW100_DST_IMG_Y_BASE2 0x48 +#define DW100_DST_IMG_UV_BASE2 0x4c +#define DW100_DST_IMG_SIZE2 0x50 +#define DW100_DST_IMG_STRIDE2 0x54 +#define DW100_SWAP_CONTROL 0x58 +#define DW100_SWAP_CONTROL_BYTE BIT(0) +#define DW100_SWAP_CONTROL_SHORT BIT(1) +#define DW100_SWAP_CONTROL_WORD BIT(2) +#define DW100_SWAP_CONTROL_LONG BIT(3) +#define DW100_SWAP_CONTROL_Y(x) (((x) & GENMASK(3, 0)) << 0) +#define DW100_SWAP_CONTROL_UV(x) (((x) & GENMASK(3, 0)) << 4) +#define DW100_SWAP_CONTROL_SRC(x) (((x) & GENMASK(7, 0)) << 0) +#define DW100_SWAP_CONTROL_DST(x) (((x) & GENMASK(7, 0)) << 8) +#define DW100_SWAP_CONTROL_SRC2(x) (((x) & GENMASK(7, 0)) << 16) +#define DW100_SWAP_CONTROL_DST2(x) (((x) & GENMASK(7, 0)) << 24) +#define DW100_SWAP_CONTROL_SRC_MASK GENMASK(7, 0) +#define DW100_SWAP_CONTROL_DST_MASK GENMASK(15, 8) +#define DW100_SWAP_CONTROL_SRC2_MASK GENMASK(23, 16) +#define DW100_SWAP_CONTROL_DST2_MASK GENMASK(31, 24) +#define DW100_VERTICAL_SPLIT_LINE 0x5c +#define DW100_HORIZON_SPLIT_LINE 0x60 +#define DW100_SCALE_FACTOR 0x64 +#define DW100_ROI_START 0x68 +#define DW100_ROI_START_X(x) (((x) & GENMASK(12, 0)) << 0) +#define DW100_ROI_START_Y(y) (((y) & GENMASK(12, 0)) << 16) +#define DW100_BOUNDARY_PIXEL 0x6c +#define DW100_BOUNDARY_PIXEL_V(v) (((v) & GENMASK(7, 0)) << 0) +#define DW100_BOUNDARY_PIXEL_U(u) (((u) & GENMASK(7, 0)) << 8) +#define DW100_BOUNDARY_PIXEL_Y(y) (((y) & GENMASK(7, 0)) << 16) + +#define DW100_INTERRUPT_STATUS 0x70 +#define DW100_INTERRUPT_STATUS_INT_FRAME_DONE BIT(0) +#define DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT BIT(1) +#define DW100_INTERRUPT_STATUS_INT_ERR_AXI_RESP BIT(2) +#define DW100_INTERRUPT_STATUS_INT_ERR_X BIT(3) +#define DW100_INTERRUPT_STATUS_INT_ERR_MB_FETCH BIT(4) +#define DW100_INTERRUPT_STATUS_INT_ERR_FRAME2 BIT(5) +#define DW100_INTERRUPT_STATUS_INT_ERR_FRAME3 BIT(6) +#define DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE BIT(7) +#define DW100_INTERRUPT_STATUS_INT_ERR_STATUS(x) (((x) >> 1) & 0x7f) +#define DW100_INTERRUPT_STATUS_INT_STATUS(x) ((x) & 0xff) + +#define DW100_INTERRUPT_STATUS_INT_ENABLE_MASK GENMASK(15, 8) +#define DW100_INTERRUPT_STATUS_INT_ENABLE(x) (((x) & GENMASK(7, 0)) << 8) +#define DW100_INTERRUPT_STATUS_FRAME_BUSY BIT(16) +#define DW100_INTERRUPT_STATUS_INT_CLEAR(x) (((x) & GENMASK(7, 0)) << 24) +#define DW100_BUS_CTRL 0x74 +#define DW100_BUS_CTRL_AXI_MASTER_ENABLE BIT(31) +#define DW100_BUS_CTRL1 0x78 +#define DW100_BUS_TIME_OUT_CYCLE 0x7c +#define DW100_DST_IMG_Y_SIZE1 0x80 +#define DW100_DST_IMG_Y_SIZE(sz) (((sz) >> 4) & GENMASK(29, 0)) +#define DW100_DST_IMG_UV_SIZE(sz) (((sz) >> 4) & GENMASK(29, 0)) +#define DW100_DST_IMG_UV_SIZE1 0x84 +#define DW100_DST_IMG_Y_SIZE2 0x88 +#define DW100_DST_IMG_UV_SIZE2 0x8c + +#endif /* _DW100_REGS_H_ */ -- cgit v1.3-14-g43fede From 3c68cc8e35f2f6ef7faf41483702354732567b7c Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Sat, 30 Jul 2022 17:48:43 +0200 Subject: media: MAINTAINERS: add entry for i.MX8MP DW100 v4l2 mem2mem driver Add myself as maintainer of the dw100 driver which offers hardware accelerated dewarping operations through a v4l2 mem2mem interface. Signed-off-by: Xavier Roumegue Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8a5012ba6ff9..16ee869e6015 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14690,6 +14690,15 @@ S: Orphan F: Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml F: drivers/nfc/nxp-nci +NXP i.MX 8MP DW100 V4L2 DRIVER +M: Xavier Roumegue +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/nxp,dw100.yaml +F: Documentation/userspace-api/media/drivers/dw100.rst +F: drivers/media/platform/nxp/dw100/ +F: include/uapi/linux/dw100.h + NXP i.MX 8QXP/8QM JPEG V4L2 DRIVER M: Mirela Rabulea R: NXP Linux Team -- cgit v1.3-14-g43fede From e6433ad04b44c4b68c4de3fc06356d3ac6104e9c Mon Sep 17 00:00:00 2001 From: Li zeming Date: Mon, 4 Jul 2022 04:24:10 +0200 Subject: media: staging/media/av7110/av7110: Fix typo in string Remove the repeated ',' from string Signed-off-by: Li zeming Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/av7110/av7110_av.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index ab7cf496b454..0bf513c26b6b 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -106,7 +106,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, int ret = 0; struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); + dprintk(2, "av7110:%p, dvb_demux_feed:%p\n", av7110, dvbdmxfeed); if (av7110->playing || (av7110->rec_mode & av)) return -EBUSY; -- cgit v1.3-14-g43fede From d063ddfb8a000a5079314821e84e333d7ce9d1e6 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 15 Jul 2022 07:16:16 +0200 Subject: media: dib8000: Fix comment typo The double `this' is duplicated in line 3215, remove one. [hverkuil: while we're at it, fix the 'succedeed' typo as well] Signed-off-by: Jason Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/dib8000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index d67f2dd997d0..fe19d127abb3 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -3212,7 +3212,7 @@ static int dib8000_tune(struct dvb_frontend *fe) case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */ if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { - /* if there is a diversity fe in input and this fe is has not already failed : wait here until this this fe has succedeed or failed */ + /* if there is a diversity fe in input and this fe is has not already failed : wait here until this fe has succeeded or failed */ if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failed also, break the current one */ -- cgit v1.3-14-g43fede From 948c752d2e27022e40fc16b9a7c611907de7e627 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 16 Jul 2022 06:31:02 +0200 Subject: media: sun6i-csi: Fix comment typo The double `the' is duplicated in the comment, remove one. Signed-off-by: Jason Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index 1d46e113d01d..74d64a20ba5b 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -177,7 +177,7 @@ static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count) /* * CSI will lookup the next dma buffer for next frame before the - * the current frame done IRQ triggered. This is not documented + * current frame done IRQ triggered. This is not documented * but reported by Ondřej Jirman. * The BSP code has workaround for this too. It skip to mark the * first buffer as frame done for VB2 and pass the second buffer -- cgit v1.3-14-g43fede From bd012eaa8a38623c163e3237a9f58bb3e25d457c Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 03:47:55 +0200 Subject: media: dvb-frontends: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda1002x.h | 2 +- drivers/media/dvb-frontends/tda10048.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h index 60a0952c1bca..00491bea9975 100644 --- a/drivers/media/dvb-frontends/tda1002x.h +++ b/drivers/media/dvb-frontends/tda1002x.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards + used on the Siemens DVB-C cards Copyright (C) 1999 Convergence Integrated Media GmbH Copyright (C) 2004 Markus Schulz diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c index d1d206ebdedd..0b3f6999515e 100644 --- a/drivers/media/dvb-frontends/tda10048.c +++ b/drivers/media/dvb-frontends/tda10048.c @@ -1118,7 +1118,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, state->pll_pfactor = 0; } - /* Establish any defaults the the user didn't pass */ + /* Establish any defaults the user didn't pass */ tda10048_establish_defaults(&state->frontend); /* Set the xtal and freq defaults */ -- cgit v1.3-14-g43fede From 3b12018d8f8c2b82200bb122d2ada021c2249726 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 03:57:07 +0200 Subject: media: cx88: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx88/cx88-dsp.c b/drivers/media/pci/cx88/cx88-dsp.c index f1e1fc1cb4bd..e378f3b215c7 100644 --- a/drivers/media/pci/cx88/cx88-dsp.c +++ b/drivers/media/pci/cx88/cx88-dsp.c @@ -24,7 +24,7 @@ /* * We calculate the baseband frequencies of the carrier and the pilot tones - * based on the the sampling rate of the audio rds fifo. + * based on the sampling rate of the audio rds fifo. */ #define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0) -- cgit v1.3-14-g43fede From 2e2b25af5e33c3771ac1b28fc0b13637679266d0 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 04:04:59 +0200 Subject: media: ivtv: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-yuv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index e79e8a5a744a..4ba10c34a16a 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -538,7 +538,7 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f) reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); /* Okay, we've wasted time working out the correct value, - but if we use it, it fouls the the window alignment. + but if we use it, it fouls the window alignment. Fudge it to what we want... */ reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); -- cgit v1.3-14-g43fede From 9efd64972443de860811269f2a98c93b90967e93 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 04:19:18 +0200 Subject: media: saa7164: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 7973ae42873a..d5f32e3ff544 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -626,7 +626,7 @@ static irqreturn_t saa7164_irq(int irq, void *dev_id) portf = &dev->ports[SAA7164_PORT_VBI2]; /* Check that the hardware is accessible. If the status bytes are - * 0xFF then the device is not accessible, the the IRQ belongs + * 0xFF then the device is not accessible, the IRQ belongs * to another driver. * 4 x u32 interrupt registers. */ -- cgit v1.3-14-g43fede From 565cdd279a3cf80d9d96c4a83293a342fb3429b8 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 08:33:41 +0200 Subject: media: platform: ti: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/davinci/vpbe.c | 2 +- drivers/media/platform/ti/omap3isp/isp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti/davinci/vpbe.c b/drivers/media/platform/ti/davinci/vpbe.c index 5f0aeb744e81..509ecc84624e 100644 --- a/drivers/media/platform/ti/davinci/vpbe.c +++ b/drivers/media/platform/ti/davinci/vpbe.c @@ -280,7 +280,7 @@ static int vpbe_set_default_output(struct vpbe_device *vpbe_dev) * vpbe_get_output - Get output * @vpbe_dev: vpbe device ptr * - * return current vpbe output to the the index + * return current vpbe output to the index */ static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev) { diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index d251736eb420..a6052df9bb19 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -1528,7 +1528,7 @@ void omap3isp_print_status(struct isp_device *isp) * To solve this problem power management support is split into prepare/complete * and suspend/resume operations. The pipelines are stopped in prepare() and the * ISP clocks get disabled in suspend(). Similarly, the clocks are re-enabled in - * resume(), and the the pipelines are restarted in complete(). + * resume(), and the pipelines are restarted in complete(). * * TODO: PM dependencies between the ISP and sensors are not modelled explicitly * yet. -- cgit v1.3-14-g43fede From 031d8af19bd2ca7343f2df610004d72dfd78386f Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 08:36:43 +0200 Subject: media: gspca: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/finepix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c index 66c8e5122a0a..bc6133b525e3 100644 --- a/drivers/media/usb/gspca/finepix.c +++ b/drivers/media/usb/gspca/finepix.c @@ -129,7 +129,7 @@ again: * for, then it's the end of the * frame. Sometimes the jpeg is not complete, * but there's nothing we can do. We also end - * here if the the jpeg ends right at the end + * here if the jpeg ends right at the end * of the frame. */ gspca_frame_add(gspca_dev, LAST_PACKET, data, len); -- cgit v1.3-14-g43fede From 33b96f15f277bff42c172f64b059a563a749495e Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 08:41:05 +0200 Subject: media: tm6000: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c index 98f4a63adc2a..b7842cd6f9af 100644 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -1297,7 +1297,7 @@ static int tm6000_usb_probe(struct usb_interface *interface, le16_to_cpu(dev->udev->descriptor.idProduct), interface->altsetting->desc.bInterfaceNumber); -/* check if the the device has the iso in endpoint at the correct place */ +/* check if the device has the iso in endpoint at the correct place */ if (!dev->isoc_in.endp) { printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); rc = -ENODEV; -- cgit v1.3-14-g43fede From fd3ed970044cdc6142c77522bc83071ff02007b1 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 09:18:50 +0200 Subject: media: v4l2-ioctl: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. [hverkuil: the the -> that the] Signed-off-by: Slark Xiao Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index c314025d977e..a1a1b51ac599 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1045,7 +1045,7 @@ static void v4l_sanitize_format(struct v4l2_format *fmt) /* * The v4l2_pix_format structure has been extended with fields that were * not previously required to be set to zero by applications. The priv - * field, when set to a magic value, indicates the the extended fields + * field, when set to a magic value, indicates that the extended fields * are valid. Otherwise they will contain undefined values. To simplify * the API towards drivers zero the extended fields and set the priv * field to the magic value when the extended pixel format structure -- cgit v1.3-14-g43fede From 73854b867e1316bac7670b2f37b31375119ae328 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 4 Aug 2022 13:51:38 +0200 Subject: media: drxk: Fix comment typo The double `for' is duplicated in the comment, remove one. Signed-off-by: Jason Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxk_hard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 9430295a8175..47d83e0a470c 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -3516,7 +3516,7 @@ static int set_dvbt_standard(struct drxk_state *state, status = write16(state, IQM_AF_CLP_LEN__A, 0); if (status < 0) goto error; - /* window size for for sense pre-SAW detection */ + /* window size for sense pre-SAW detection */ status = write16(state, IQM_AF_SNS_LEN__A, 0); if (status < 0) goto error; -- cgit v1.3-14-g43fede From 85b1dedd556e372f8f7adc47a0be61d3d9af134c Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 4 Aug 2022 13:58:25 +0200 Subject: media: technisat-usb2: Fix comment typo The double `is' is duplicated in the comment, remove one. Signed-off-by: Jason Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/technisat-usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index 9c77911fcad4..df90c6c5f3b9 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -786,7 +786,7 @@ static void technisat_usb2_disconnect(struct usb_interface *intf) { struct dvb_usb_device *dev = usb_get_intfdata(intf); - /* work and stuff was only created when the device is is hot-state */ + /* work and stuff was only created when the device is hot-state */ if (dev != NULL) { struct technisat_usb2_state *state = dev->priv; if (state != NULL) -- cgit v1.3-14-g43fede From 54db159d766b73307a2e5aee77f76c971cb180e3 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 4 Aug 2022 14:01:48 +0200 Subject: media: v4l2-flash: Fix comment typo The double `the' is duplicated in the comment, remove one. Signed-off-by: Jason Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-flash-led-class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index e70e128ccc9c..355595a0fefa 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -94,7 +94,7 @@ static int v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash, * brightness <-> intensity conversion, it also must have defined * related v4l2 control step == 1. In such a case a backward conversion * from led brightness to v4l2 intensity is required to find out the - * the aligned intensity value. + * aligned intensity value. */ if (has_flash_op(v4l2_flash, led_brightness_to_intensity)) ctrl->val = call_flash_op(v4l2_flash, -- cgit v1.3-14-g43fede From b78476917ff25f172f1388aae0eebb9c8f7c5836 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Sun, 21 Aug 2022 17:15:52 +0200 Subject: media: i2c/cx25840: fix repeated words in comments Delete the redundant word 'of'. Signed-off-by: wangjianli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index 9d7d1d149f1a..8cef9656c612 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -196,7 +196,7 @@ static u32 clock_divider_to_resolution(u16 divider) { /* * Resolution is the duration of 1 tick of the readable portion of - * of the pulse width counter as read from the FIFO. The two lsb's are + * the pulse width counter as read from the FIFO. The two lsb's are * not readable, hence the << 2. This function returns ns. */ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, -- cgit v1.3-14-g43fede From 08ebb1c0da84bbec6df105a60b339e40fba241c9 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Sun, 21 Aug 2022 17:17:41 +0200 Subject: media: pci/cx18: fix repeated words in comments Delete the redundant word 'of'. Signed-off-by: wangjianli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c index fdac310d7477..1b038b2802bf 100644 --- a/drivers/media/pci/cx18/cx18-firmware.c +++ b/drivers/media/pci/cx18/cx18-firmware.c @@ -248,7 +248,7 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) * * Many thanks to Jeff Campbell and Mike Bradley for their extensive * investigation, experimentation, testing, and suggested solutions of - * of audio/video sync problems with SVideo and CVBS captures. + * audio/video sync problems with SVideo and CVBS captures. */ /* the fast clock is at 200/245 MHz */ -- cgit v1.3-14-g43fede From 7f2597fe945eb5b622afe1aaad2c6c4ca403ebd8 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Sun, 21 Aug 2022 17:21:38 +0200 Subject: media: pci/cx18: fix repeated words in comments Delete the redundant word 'of'. Signed-off-by: wangjianli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-av-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-av-audio.c b/drivers/media/pci/cx18/cx18-av-audio.c index 833baa934448..78e05df9a7ba 100644 --- a/drivers/media/pci/cx18/cx18-av-audio.c +++ b/drivers/media/pci/cx18/cx18-av-audio.c @@ -50,7 +50,7 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) * * Many thanks to Jeff Campbell and Mike Bradley for their extensive * investigation, experimentation, testing, and suggested solutions of - * of audio/video sync problems with SVideo and CVBS captures. + * audio/video sync problems with SVideo and CVBS captures. */ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { -- cgit v1.3-14-g43fede From 13c3af0c646251edc73164a310da31e7c6156659 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Sun, 21 Aug 2022 17:25:04 +0200 Subject: media: pci/cx23885: fix repeated words in comments Delete the redundant word 'of'. Signed-off-by: wangjianli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23888-ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index ddfd2eb37484..222d04421468 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -235,7 +235,7 @@ static u32 clock_divider_to_resolution(u16 divider) { /* * Resolution is the duration of 1 tick of the readable portion of - * of the pulse width counter as read from the FIFO. The two lsb's are + * the pulse width counter as read from the FIFO. The two lsb's are * not readable, hence the << 2. This function returns ns. */ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, -- cgit v1.3-14-g43fede From 8847fb9b965cb5482067b5fbda17f5d2b17f532b Mon Sep 17 00:00:00 2001 From: wangjianli Date: Tue, 23 Aug 2022 16:42:19 +0200 Subject: media: ti/omap: fix repeated words in comments Delete the redundant word 'the'. Signed-off-by: wangjianli Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/omap/omap_voutlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/omap/omap_voutlib.c b/drivers/media/platform/ti/omap/omap_voutlib.c index fdea2309ee37..0ac46458e41c 100644 --- a/drivers/media/platform/ti/omap/omap_voutlib.c +++ b/drivers/media/platform/ti/omap/omap_voutlib.c @@ -107,7 +107,7 @@ EXPORT_SYMBOL_GPL(omap_vout_try_window); /* Given a new render window in new_win, adjust the window to the * nearest supported configuration. The image cropping window in crop * will also be adjusted if necessary. Preference is given to keeping the - * the window as close to the requested configuration as possible. If + * window as close to the requested configuration as possible. If * successful, new_win, vout->win, and crop are updated. * Returns zero if successful, or -EINVAL if the requested preview window is * impossible and cannot reasonably be adjusted. -- cgit v1.3-14-g43fede From c0a80d5c1091c83281360927aec72d8fdc79f3c6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 13:54:42 +0200 Subject: media: zoran: fix checkpatch --strict issues Prepare for moving this driver to mainline by fixing the remaining checkpatch issues. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/videocodec.c | 7 +- drivers/staging/media/zoran/videocodec.h | 190 ++++++++++++++--------------- drivers/staging/media/zoran/zoran.h | 30 +++-- drivers/staging/media/zoran/zoran_card.c | 49 ++++---- drivers/staging/media/zoran/zoran_card.h | 9 +- drivers/staging/media/zoran/zoran_device.c | 37 +++--- drivers/staging/media/zoran/zoran_device.h | 44 +++---- drivers/staging/media/zoran/zoran_driver.c | 57 +-------- drivers/staging/media/zoran/zr36016.c | 142 +++++++++------------ drivers/staging/media/zoran/zr36050.c | 182 +++++++++++++-------------- drivers/staging/media/zoran/zr36057.h | 130 ++++++++++---------- drivers/staging/media/zoran/zr36060.c | 7 +- drivers/staging/media/zoran/zr36060.h | 86 ++++++------- 13 files changed, 449 insertions(+), 521 deletions(-) diff --git a/drivers/staging/media/zoran/videocodec.c b/drivers/staging/media/zoran/videocodec.c index a0c8bde5ec11..8efc5e06b0f7 100644 --- a/drivers/staging/media/zoran/videocodec.c +++ b/drivers/staging/media/zoran/videocodec.c @@ -92,9 +92,8 @@ struct videocodec *videocodec_attach(struct videocodec_master *master) h->attached += 1; return codec; - } else { - kfree(codec); } + kfree(codec); } h = h->next; } @@ -255,8 +254,8 @@ int videocodec_debugfs_show(struct seq_file *m) struct codec_list *h = codeclist_top; struct attached_list *a; - seq_printf(m, "lave or attached aster name type flags magic "); - seq_printf(m, "(connected as)\n"); + seq_puts(m, "lave or attached aster name type flags magic "); + seq_puts(m, "(connected as)\n"); while (h) { seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", diff --git a/drivers/staging/media/zoran/videocodec.h b/drivers/staging/media/zoran/videocodec.h index 5e6057edd339..6b69f69667f9 100644 --- a/drivers/staging/media/zoran/videocodec.h +++ b/drivers/staging/media/zoran/videocodec.h @@ -12,109 +12,109 @@ /* general description */ /* =================== */ -/* Should ease the (re-)usage of drivers supporting cards with (different) - video codecs. The codecs register to this module their functionality, - and the processors (masters) can attach to them if they fit. - - The codecs are typically have a "strong" binding to their master - so I - don't think it makes sense to have a full blown interfacing as with e.g. - i2c. If you have an other opinion, let's discuss & implement it :-))) - - Usage: - - The slave has just to setup the videocodec structure and use two functions: - videocodec_register(codecdata); - videocodec_unregister(codecdata); - The best is just calling them at module (de-)initialisation. - - The master sets up the structure videocodec_master and calls: - codecdata=videocodec_attach(master_codecdata); - videocodec_detach(codecdata); - - The slave is called during attach/detach via functions setup previously - during register. At that time, the master_data pointer is set up - and the slave can access any io registers of the master device (in the case - the slave is bound to it). Otherwise it doesn't need this functions and - therfor they may not be initialized. - - The other functions are just for convenience, as they are for sure used by - most/all of the codecs. The last ones may be omitted, too. - - See the structure declaration below for more information and which data has - to be set up for the master and the slave. - - ---------------------------------------------------------------------------- - The master should have "knowledge" of the slave and vice versa. So the data - structures sent to/from slave via set_data/get_data set_image/get_image are - device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) - ---------------------------------------------------------------------------- -*/ +/* + * Should ease the (re-)usage of drivers supporting cards with (different) + * video codecs. The codecs register to this module their functionality, + * and the processors (masters) can attach to them if they fit. + * + * The codecs are typically have a "strong" binding to their master - so I + * don't think it makes sense to have a full blown interfacing as with e.g. + * i2c. If you have an other opinion, let's discuss & implement it :-))) + * + * Usage: + * + * The slave has just to setup the videocodec structure and use two functions: + * videocodec_register(codecdata); + * videocodec_unregister(codecdata); + * The best is just calling them at module (de-)initialisation. + * + * The master sets up the structure videocodec_master and calls: + * codecdata=videocodec_attach(master_codecdata); + * videocodec_detach(codecdata); + * + * The slave is called during attach/detach via functions setup previously + * during register. At that time, the master_data pointer is set up + * and the slave can access any io registers of the master device (in the case + * the slave is bound to it). Otherwise it doesn't need this functions and + * therefor they may not be initialized. + * + * The other functions are just for convenience, as they are for sure used by + * most/all of the codecs. The last ones may be omitted, too. + * + * See the structure declaration below for more information and which data has + * to be set up for the master and the slave. + * + * ---------------------------------------------------------------------------- + * The master should have "knowledge" of the slave and vice versa. So the data + * structures sent to/from slave via set_data/get_data set_image/get_image are + * device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) + * ---------------------------------------------------------------------------- + */ /* ========================================== */ /* description of the videocodec_io structure */ /* ========================================== */ /* - ==== master setup ==== - name -> name of the device structure for reference and debugging - master_data -> data ref. for the master (e.g. the zr36055,57,67) - readreg -> ref. to read-fn from register (setup by master, used by slave) - writereg -> ref. to write-fn to register (setup by master, used by slave) - this two functions do the lowlevel I/O job - - ==== slave functionality setup ==== - slave_data -> data ref. for the slave (e.g. the zr36050,60) - check -> fn-ref. checks availability of an device, returns -EIO on failure or - the type on success - this makes espcecially sense if a driver module supports more than - one codec which may be quite similar to access, nevertheless it - is good for a first functionality check - - -- main functions you always need for compression/decompression -- - - set_mode -> this fn-ref. resets the entire codec, and sets up the mode - with the last defined norm/size (or device default if not - available) - it returns 0 if the mode is possible - set_size -> this fn-ref. sets the norm and image size for - compression/decompression (returns 0 on success) - the norm param is defined in videodev2.h (V4L2_STD_*) - - additional setup may be available, too - but the codec should work with - some default values even without this - - set_data -> sets device-specific data (tables, quality etc.) - get_data -> query device-specific data (tables, quality etc.) - - if the device delivers interrupts, they may be setup/handled here - setup_interrupt -> codec irq setup (not needed for 36050/60) - handle_interrupt -> codec irq handling (not needed for 36050/60) - - if the device delivers pictures, they may be handled here - put_image -> puts image data to the codec (not needed for 36050/60) - get_image -> gets image data from the codec (not needed for 36050/60) - the calls include frame numbers and flags (even/odd/...) - if needed and a flag which allows blocking until its ready -*/ + * ==== master setup ==== + * name -> name of the device structure for reference and debugging + * master_data -> data ref. for the master (e.g. the zr36055,57,67) + * readreg -> ref. to read-fn from register (setup by master, used by slave) + * writereg -> ref. to write-fn to register (setup by master, used by slave) + * this two functions do the lowlevel I/O job + * + * ==== slave functionality setup ==== + * slave_data -> data ref. for the slave (e.g. the zr36050,60) + * check -> fn-ref. checks availability of an device, returns -EIO on failure or + * the type on success + * this makes espcecially sense if a driver module supports more than + * one codec which may be quite similar to access, nevertheless it + * is good for a first functionality check + * + * -- main functions you always need for compression/decompression -- + * + * set_mode -> this fn-ref. resets the entire codec, and sets up the mode + * with the last defined norm/size (or device default if not + * available) - it returns 0 if the mode is possible + * set_size -> this fn-ref. sets the norm and image size for + * compression/decompression (returns 0 on success) + * the norm param is defined in videodev2.h (V4L2_STD_*) + * + * additional setup may be available, too - but the codec should work with + * some default values even without this + * + * set_data -> sets device-specific data (tables, quality etc.) + * get_data -> query device-specific data (tables, quality etc.) + * + * if the device delivers interrupts, they may be setup/handled here + * setup_interrupt -> codec irq setup (not needed for 36050/60) + * handle_interrupt -> codec irq handling (not needed for 36050/60) + + * if the device delivers pictures, they may be handled here + * put_image -> puts image data to the codec (not needed for 36050/60) + * get_image -> gets image data from the codec (not needed for 36050/60) + * the calls include frame numbers and flags (even/odd/...) + * if needed and a flag which allows blocking until its ready + */ /* ============== */ /* user interface */ /* ============== */ /* - Currently there is only a information display planned, as the layer - is not visible for the user space at all. - - Information is available via procfs. The current entry is "/proc/videocodecs" - but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. - -A example for such an output is: - -lave or attached aster name type flags magic (connected as) -S zr36050 0002 0000d001 00000000 (TEMPLATE) -M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) -M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) - -*/ + * Currently there is only a information display planned, as the layer + * is not visible for the user space at all. + * + * Information is available via procfs. The current entry is "/proc/videocodecs" + * but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. + * + * A example for such an output is: + * + * lave or attached aster name type flags magic (connected as) + * S zr36050 0002 0000d001 00000000 (TEMPLATE) + * M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) + * M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) + */ /* =============================================== */ /* special defines for the videocodec_io structure */ @@ -293,15 +293,15 @@ struct videocodec_master { // * master structure needs to be kmalloc'ed before calling attach // and free'd after calling detach // * returns pointer on success, NULL on failure -extern struct videocodec *videocodec_attach(struct videocodec_master *); +struct videocodec *videocodec_attach(struct videocodec_master *master); // * 0 on success, <0 (errno) on failure -extern int videocodec_detach(struct videocodec *); +int videocodec_detach(struct videocodec *codec); /* register and unregister commands for the slaves */ // * 0 on success, <0 (errno) on failure -extern int videocodec_register(const struct videocodec *); +int videocodec_register(const struct videocodec *codec); // * 0 on success, <0 (errno) on failure -extern int videocodec_unregister(const struct videocodec *); +int videocodec_unregister(const struct videocodec *codec); /* the other calls are directly done via the videocodec structure! */ diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h index 05227e5298f6..56340553b282 100644 --- a/drivers/staging/media/zoran/zoran.h +++ b/drivers/staging/media/zoran/zoran.h @@ -140,11 +140,16 @@ struct zoran_v4l_settings { /* jpg-capture/-playback settings */ struct zoran_jpg_settings { - int decimation; /* this bit is used to set everything to default */ - int hor_dcm, ver_dcm, tmp_dcm; /* capture decimation settings (tmp_dcm=1 means both fields) */ - int field_per_buff, odd_even; /* field-settings (odd_even=1 (+tmp_dcm=1) means top-field-first) */ - int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */ - struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */ + /* this bit is used to set everything to default */ + int decimation; + /* capture decimation settings (tmp_dcm=1 means both fields) */ + int hor_dcm, ver_dcm, tmp_dcm; + /* field-settings (odd_even=1 (+tmp_dcm=1) means top-field-first) */ + int field_per_buff, odd_even; + /* crop settings (subframe capture) */ + int img_x, img_y, img_width, img_height; + /* JPEG-specific capture settings */ + struct v4l2_jpegcompression jpg_comp; }; struct zoran; @@ -248,7 +253,8 @@ struct zoran { unsigned long vbseq; /* zr36057's code buffer table */ - __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + __le32 *stat_com; /* Additional stuff for testing */ unsigned int ghost_int; @@ -292,14 +298,16 @@ static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct zoran, v4l2_dev); } -/* There was something called _ALPHA_BUZ that used the PCI address instead of - * the kernel iomapped address for btread/btwrite. */ +/* + * There was something called _ALPHA_BUZ that used the PCI address instead of + * the kernel iomapped address for btread/btwrite. + */ #define btwrite(dat, adr) writel((dat), zr->zr36057_mem + (adr)) #define btread(adr) readl(zr->zr36057_mem + (adr)) -#define btand(dat, adr) btwrite((dat) & btread(adr), adr) -#define btor(dat, adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat, mask, adr) btwrite((dat) | ((mask) & btread(adr)), adr) +#define btand(dat, adr) btwrite((dat) & btread(adr), (adr)) +#define btor(dat, adr) btwrite((dat) | btread(adr), (adr)) +#define btaor(dat, mask, adr) btwrite((dat) | ((mask) & btread(adr)), (adr)) #endif diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c index 26f978a1cc72..869aabde3bef 100644 --- a/drivers/staging/media/zoran/zoran_card.c +++ b/drivers/staging/media/zoran/zoran_card.c @@ -172,8 +172,6 @@ void zr36016_write(struct videocodec *codec, u16 reg, u32 val) static void dc10_init(struct zoran *zr) { - pci_dbg(zr->pci_dev, "%s\n", __func__); - /* Pixel clock selection */ GPIO(zr, 4, 0); GPIO(zr, 5, 1); @@ -183,13 +181,10 @@ static void dc10_init(struct zoran *zr) static void dc10plus_init(struct zoran *zr) { - pci_dbg(zr->pci_dev, "%s\n", __func__); } static void buz_init(struct zoran *zr) { - pci_dbg(zr->pci_dev, "%s\n", __func__); - /* some stuff from Iomega */ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); @@ -198,8 +193,6 @@ static void buz_init(struct zoran *zr) static void lml33_init(struct zoran *zr) { - pci_dbg(zr->pci_dev, "%s\n", __func__); - GPIO(zr, 2, 1); // Set Composite input/output } @@ -334,10 +327,6 @@ static void videocodec_exit(struct zoran *zr) codec_exit(zr, zr->card.video_vfe); } -// struct tvnorm { -// u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; -// }; - static const struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; static const struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; static const struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; @@ -619,7 +608,10 @@ static struct card_info zoran_cards[NUM_CARDS] = { }, { .type = AVS6EYES, .name = "6-Eyes", -/* AverMedia chose not to brand the 6-Eyes. Thus it can't be autodetected, and requires card=x. */ + /* + * AverMedia chose not to brand the 6-Eyes. Thus it can't be + * autodetected, and requires card=x. + */ .i2c_decoder = "ks0127", .addrs_decoder = ks0127_addrs, .i2c_encoder = "bt866", @@ -764,7 +756,9 @@ int zoran_check_jpg_settings(struct zoran *zr, case 4: if (zr->card.type == DC10_NEW) { - pci_dbg(zr->pci_dev, "%s - HDec by 4 is not supported on the DC10\n", __func__); + pci_dbg(zr->pci_dev, + "%s - HDec by 4 is not supported on the DC10\n", + __func__); err0++; break; } @@ -1019,7 +1013,9 @@ static int zr36057_init(struct zoran *zr) zr->timing = zr->card.tvn[ZR_NORM_SECAM]; } if (!zr->timing) { - pci_warn(zr->pci_dev, "%s - default TV standard not supported by hardware. PAL will be used.\n", __func__); + pci_warn(zr->pci_dev, + "%s - default TV standard not supported by hardware. PAL will be used.\n", + __func__); zr->norm = V4L2_STD_PAL; zr->timing = zr->card.tvn[ZR_NORM_PAL]; } @@ -1038,9 +1034,9 @@ static int zr36057_init(struct zoran *zr) zr->stat_com = dma_alloc_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), &zr->p_sc, GFP_KERNEL); - if (!zr->stat_com) { + if (!zr->stat_com) return -ENOMEM; - } + for (j = 0; j < BUZ_NUM_STAT_COM; j++) zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ @@ -1066,9 +1062,11 @@ static int zr36057_init(struct zoran *zr) return 0; exit_statcomb: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, + zr->stat_comb, zr->p_scb); exit_statcom: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), zr->stat_com, zr->p_sc); + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), + zr->stat_com, zr->p_sc); return err; } @@ -1099,8 +1097,10 @@ static void zoran_remove(struct pci_dev *pdev) btwrite(0, ZR36057_SPGPPCR); pci_free_irq(zr->pci_dev, 0, zr); /* unmap and free memory */ - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), zr->stat_com, zr->p_sc); - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), + zr->stat_com, zr->p_sc); + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, + zr->stat_comb, zr->p_scb); pci_release_regions(pdev); pci_disable_device(zr->pci_dev); zoran_exit_video_devices(zr); @@ -1299,7 +1299,8 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_err(pdev, "Unknown card, try specifying card=X module parameter\n"); goto zr_unreg; } - pci_info(zr->pci_dev, "%s() - card %s detected\n", __func__, zoran_cards[card_num].name); + pci_info(zr->pci_dev, "%s() - card %s detected\n", __func__, + zoran_cards[card_num].name); } else { card_num = card[nr]; if (card_num >= NUM_CARDS || card_num < 0) { @@ -1324,7 +1325,8 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto zr_unreg; - zr->zr36057_mem = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + zr->zr36057_mem = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); if (!zr->zr36057_mem) { pci_err(pdev, "%s() - ioremap failed\n", __func__); goto zr_pci_release; @@ -1348,7 +1350,8 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) &latency); need_latency = zr->revision > 1 ? 32 : 48; if (latency != need_latency) { - pci_info(zr->pci_dev, "Changing PCI latency from %d to %d\n", latency, need_latency); + pci_info(zr->pci_dev, "Changing PCI latency from %d to %d\n", + latency, need_latency); pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); } diff --git a/drivers/staging/media/zoran/zoran_card.h b/drivers/staging/media/zoran/zoran_card.h index 8e0d634cb30f..518cb426b446 100644 --- a/drivers/staging/media/zoran/zoran_card.h +++ b/drivers/staging/media/zoran/zoran_card.h @@ -19,11 +19,10 @@ extern int zr36067_debug; extern const struct video_device zoran_template; -extern int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, - int try); -extern void zoran_open_init_params(struct zoran *zr); -extern void zoran_vdev_release(struct video_device *vdev); +int zoran_check_jpg_settings(struct zoran *zr, + struct zoran_jpg_settings *settings, int try); +void zoran_open_init_params(struct zoran *zr); +void zoran_vdev_release(struct video_device *vdev); void zr36016_write(struct videocodec *codec, u16 reg, u32 val); diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c index 2470889a58fa..31f049b55529 100644 --- a/drivers/staging/media/zoran/zoran_device.c +++ b/drivers/staging/media/zoran/zoran_device.c @@ -50,7 +50,6 @@ static bool lml33dpath; /* default = 0 module_param(lml33dpath, bool, 0644); MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)"); -int zr_set_buf(struct zoran *zr); /* * initialize video front end */ @@ -108,7 +107,6 @@ int post_office_wait(struct zoran *zr) { u32 por; -// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_PO_PEN | ZR36057_POR_PO_TIME)) == ZR36057_POR_PO_PEN) { while ((por = btread(ZR36057_POR)) & ZR36057_POR_PO_PEN) { /* wait for something to happen */ /* TODO add timeout */ @@ -155,10 +153,12 @@ void jpeg_codec_sleep(struct zoran *zr, int sleep) { GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); if (!sleep) { - pci_dbg(zr->pci_dev, "%s() - wake GPIO=0x%08x\n", __func__, btread(ZR36057_GPPGCR1)); - udelay(500); + pci_dbg(zr->pci_dev, "%s() - wake GPIO=0x%08x\n", + __func__, btread(ZR36057_GPPGCR1)); + usleep_range(500, 1000); } else { - pci_dbg(zr->pci_dev, "%s() - sleep GPIO=0x%08x\n", __func__, btread(ZR36057_GPPGCR1)); + pci_dbg(zr->pci_dev, "%s() - sleep GPIO=0x%08x\n", + __func__, btread(ZR36057_GPPGCR1)); udelay(2); } } @@ -284,7 +284,8 @@ static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, vcrop1 = (tvn->ha / 2 - he) / 2; vcrop2 = tvn->ha / 2 - he - vcrop1; v_start = tvn->v_start; - v_end = v_start + tvn->ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP + // FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP + v_end = v_start + tvn->ha / 2; // - 1; v_start += vcrop1; v_end -= vcrop2; reg = ((v_start & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_START) @@ -298,10 +299,12 @@ static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, reg |= (hor_dcm << ZR36057_VFESPFR_HOR_DCM); reg |= (ver_dcm << ZR36057_VFESPFR_VER_DCM); reg |= (disp_mode << ZR36057_VFESPFR_DISP_MODE); - /* RJ: I don't know, why the following has to be the opposite + /* + * RJ: I don't know, why the following has to be the opposite * of the corresponding ZR36060 setting, but only this way - * we get the correct colors when uncompressing to the screen */ - //reg |= ZR36057_VFESPFR_VCLK_POL; /**/ + * we get the correct colors when uncompressing to the screen + */ + //reg |= ZR36057_VFESPFR_VCLK_POL; /* RJ: Don't know if that is needed for NTSC also */ if (!(zr->norm & V4L2_STD_NTSC)) reg |= ZR36057_VFESPFR_EXT_FL; // NEEDED!!!!!!! Wolfgang @@ -342,7 +345,7 @@ void zr36057_set_memgrab(struct zoran *zr, int mode) * will be stuck at 1 until capturing is turned back on. */ if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) - pci_warn(zr->pci_dev, "zr36057_set_memgrab(1) with SnapShot on!?\n"); + pci_warn(zr->pci_dev, "%s(1) with SnapShot on!?\n", __func__); /* switch on VSync interrupts */ btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts @@ -595,11 +598,9 @@ void jpeg_start(struct zoran *zr) /* enable the Go generation */ btor(ZR36057_JMC_GO_EN, ZR36057_JMC); - udelay(30); + usleep_range(30, 100); set_frame(zr, 1); // /FRAME - - pci_dbg(zr->pci_dev, "jpeg_start\n"); } void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) @@ -803,8 +804,10 @@ static void zoran_reap_stat_com(struct zoran *zr) unsigned int size = 0; u32 fcnt; - /* In motion decompress we don't have a hardware frame counter, - * we just count the interrupts here */ + /* + * In motion decompress we don't have a hardware frame counter, + * we just count the interrupts here + */ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) zr->jpg_seq_num++; @@ -938,9 +941,9 @@ void zoran_init_hardware(struct zoran *zr) void zr36057_restart(struct zoran *zr) { btwrite(0, ZR36057_SPGPPCR); - udelay(1000); + usleep_range(1000, 2000); btor(ZR36057_SPGPPCR_SOFT_RESET, ZR36057_SPGPPCR); - udelay(1000); + usleep_range(1000, 2000); /* assert P_Reset */ btwrite(0, ZR36057_JPC); diff --git a/drivers/staging/media/zoran/zoran_device.h b/drivers/staging/media/zoran/zoran_device.h index 322b04c55d41..34fd5cc914eb 100644 --- a/drivers/staging/media/zoran/zoran_device.h +++ b/drivers/staging/media/zoran/zoran_device.h @@ -13,37 +13,37 @@ #define __ZORAN_DEVICE_H__ /* general purpose I/O */ -extern void GPIO(struct zoran *zr, int bit, unsigned int value); +void GPIO(struct zoran *zr, int bit, unsigned int value); /* codec (or actually: guest bus) access */ -extern int post_office_wait(struct zoran *zr); -extern int post_office_write(struct zoran *zr, unsigned int guest, unsigned int reg, unsigned int value); -extern int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg); +int post_office_wait(struct zoran *zr); +int post_office_write(struct zoran *zr, unsigned int guest, unsigned int reg, + unsigned int value); +int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg); -extern void jpeg_codec_sleep(struct zoran *zr, int sleep); -extern int jpeg_codec_reset(struct zoran *zr); +void jpeg_codec_sleep(struct zoran *zr, int sleep); +int jpeg_codec_reset(struct zoran *zr); /* zr360x7 access to raw capture */ -extern void zr36057_overlay(struct zoran *zr, int on); -extern void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); -extern void zr36057_set_memgrab(struct zoran *zr, int mode); -extern int wait_grab_pending(struct zoran *zr); +void zr36057_overlay(struct zoran *zr, int on); +void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); +void zr36057_set_memgrab(struct zoran *zr, int mode); +int wait_grab_pending(struct zoran *zr); /* interrupts */ -extern void print_interrupts(struct zoran *zr); -extern void clear_interrupt_counters(struct zoran *zr); -extern irqreturn_t zoran_irq(int irq, void *dev_id); +void print_interrupts(struct zoran *zr); +void clear_interrupt_counters(struct zoran *zr); +irqreturn_t zoran_irq(int irq, void *dev_id); /* JPEG codec access */ -extern void jpeg_start(struct zoran *zr); -extern void zr36057_enable_jpg(struct zoran *zr, - enum zoran_codec_mode mode); -extern void zoran_feed_stat_com(struct zoran *zr); +void jpeg_start(struct zoran *zr); +void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode); +void zoran_feed_stat_com(struct zoran *zr); /* general */ -extern void zoran_set_pci_master(struct zoran *zr, int set_master); -extern void zoran_init_hardware(struct zoran *zr); -extern void zr36057_restart(struct zoran *zr); +void zoran_set_pci_master(struct zoran *zr, int set_master); +void zoran_init_hardware(struct zoran *zr); +void zr36057_restart(struct zoran *zr); extern const struct zoran_format zoran_formats[]; @@ -53,8 +53,8 @@ extern int pass_through; /* i2c */ #define decoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->decoder, o, f, ##args) + v4l2_subdev_call((zr)->decoder, o, f, ##args) #define encoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->encoder, o, f, ##args) + v4l2_subdev_call((zr)->encoder, o, f, ##args) #endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 4304b7e21709..212b6d1f16c9 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -203,7 +203,6 @@ static int zoran_v4l_set_format(struct zoran *zr, int width, int height, static int zoran_set_norm(struct zoran *zr, v4l2_std_id norm) { - if (!(norm & zr->card.norms)) { pci_dbg(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); return -EINVAL; @@ -287,17 +286,6 @@ static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); } -#if 0 -/* TODO: output does not work yet */ -static int zoran_enum_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran *zr = video_drvdata(file); - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK); -} -#endif - static int zoran_g_fmt_vid_out(struct file *file, void *__fh, struct v4l2_format *fmt) { @@ -430,8 +418,10 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, fmt->fmt.pix.field = V4L2_FIELD_TOP; bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); - v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, - &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); + v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, + bpp == 2 ? 1 : 2, + &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, + 0, 0); fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * bpp; fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height; return 0; @@ -627,38 +617,6 @@ static int zoran_s_input(struct file *file, void *__fh, unsigned int input) return res; } -#if 0 -/* TODO: output does not work yet */ -static int zoran_enum_output(struct file *file, void *__fh, - struct v4l2_output *outp) -{ - if (outp->index != 0) - return -EINVAL; - - outp->index = 0; - outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; - outp->std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - outp->capabilities = V4L2_OUT_CAP_STD; - strscpy(outp->name, "Autodetect", sizeof(outp->name)); - - return 0; -} -static int zoran_g_output(struct file *file, void *__fh, unsigned int *output) -{ - *output = 0; - - return 0; -} - -static int zoran_s_output(struct file *file, void *__fh, unsigned int output) -{ - if (output != 0) - return -EINVAL; - - return 0; -} -#endif - /* cropping (sub-frame capture) */ static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) { @@ -746,9 +704,6 @@ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_enum_input = zoran_enum_input, .vidioc_g_input = zoran_g_input, .vidioc_s_input = zoran_s_input, -/* .vidioc_enum_output = zoran_enum_output, - .vidioc_g_output = zoran_g_output, - .vidioc_s_output = zoran_s_output,*/ .vidioc_g_std = zoran_g_std, .vidioc_s_std = zoran_s_std, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -760,13 +715,9 @@ static const struct v4l2_ioctl_ops zoran_ioctl_ops = { .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, -/* .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,*/ .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, -/* .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,*/ .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, -/* .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,*/ .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, -/* .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,*/ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/staging/media/zoran/zr36016.c b/drivers/staging/media/zoran/zr36016.c index 0e0532537a3e..4b328ad6083f 100644 --- a/drivers/staging/media/zoran/zr36016.c +++ b/drivers/staging/media/zoran/zr36016.c @@ -15,18 +15,19 @@ /* codec io API */ #include "videocodec.h" -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ +/* + * it doesn't make sense to have more than 20 or so, + * just to prevent some unwanted loops + */ #define MAX_CODECS 20 /* amount of chips attached via this driver */ static int zr36016_codecs; -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ +/* + * Local hardware I/O functions: read/write via codec layer + * (registers are located in the master device) + */ /* read and write functions */ static u8 zr36016_read(struct zr36016 *ptr, u16 reg) @@ -58,9 +59,12 @@ static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value) zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); } -/* indirect read and write functions */ -/* the 016 supports auto-addr-increment, but - * writing it all time cost not much and is safer... */ +/* + * indirect read and write functions + * + * the 016 supports auto-addr-increment, but + * writing it all time cost not much and is safer... + */ static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) { u8 value = 0; @@ -68,8 +72,8 @@ static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) /* just in case something is wrong... */ if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); + value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; } else { zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name); } @@ -88,18 +92,14 @@ static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value) /* just in case something is wrong... */ if (ptr->codec->master_data->writereg) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); + ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); } else { zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name); } } -/* ========================================================================= - Local helper function: - - version read - ========================================================================= */ +/* Local helper function: version read */ /* version kept in datastructure */ static u8 zr36016_read_version(struct zr36016 *ptr) @@ -108,11 +108,10 @@ static u8 zr36016_read_version(struct zr36016 *ptr) return ptr->version; } -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from PAX-Lo register - ========================================================================= */ +/* + * Local helper function: basic test of "connectivity", writes/reads + * to/from PAX-Lo register + */ static int zr36016_basic_test(struct zr36016 *ptr) { @@ -150,36 +149,7 @@ static int zr36016_basic_test(struct zr36016 *ptr) return 0; /* looks good! */ } -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - NO USE -- - ========================================================================= */ - -#if 0 -static int zr36016_pushit(struct zr36016 *ptr, - u16 startreg, - u16 len, - const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", - ptr->name, startreg, len); - while (i < len) { - zr36016_writei(ptr, startreg++, data[i++]); - } - - return i; -} -#endif - -/* ========================================================================= - Basic datasets & init: - - //TODO// - ========================================================================= */ +/* Basic datasets & init */ static void zr36016_init(struct zr36016 *ptr) { @@ -213,14 +183,16 @@ static void zr36016_init(struct zr36016 *ptr) zr36016_write(ptr, ZR016_GOSTOP, 1); } -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ +/* + * CODEC API FUNCTIONS + * + * These functions are accessed by the master via the API structure + */ -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ +/* + * set compression/expansion mode and launches codec - + * this should be the last call from the master before starting processing + */ static int zr36016_set_mode(struct videocodec *codec, int mode) { struct zr36016 *ptr = (struct zr36016 *)codec->data; @@ -249,22 +221,28 @@ static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm cap->x, cap->y, cap->width, cap->height, cap->decimation); - /* if () return -EINVAL; + /* + * if () return -EINVAL; * trust the master driver that it knows what it does - so - * we allow invalid startx/y for now ... */ + * we allow invalid startx/y for now ... + */ ptr->width = cap->width; ptr->height = cap->height; - /* (Ronald) This is ugly. zoran_device.c, line 387 + /* + * (Ronald) This is ugly. zoran_device.c, line 387 * already mentions what happens if h_start is even * (blue faces, etc., cr/cb inversed). There's probably * some good reason why h_start is 0 instead of 1, so I'm * leaving it to this for now, but really... This can be - * done a lot simpler */ + * done a lot simpler + */ ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x; - /* Something to note here (I don't understand it), setting + /* + * Something to note here (I don't understand it), setting * v_start too high will cause the codec to 'not work'. I * really don't get it. values of 16 (v_start) already break - * it here. Just '0' seems to work. More testing needed! */ + * it here. Just '0' seems to work. More testing needed! + */ ptr->yoff = norm->v_start + cap->y; /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; @@ -319,11 +297,11 @@ static int zr36016_control(struct videocodec *codec, int type, int size, void *d return size; } -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ +/* + * Exit and unregister function: + * + * Deinitializes Zoran's JPEG processor + */ static int zr36016_unset(struct videocodec *codec) { @@ -344,14 +322,14 @@ static int zr36016_unset(struct videocodec *codec) return -EFAULT; } -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ +/* + * Setup and registry function: + * + * Initializes Zoran's JPEG processor + * + * Also sets pixel size, average code size, mode (compr./decompr.) + * (the given size is determined by the processor with the video interface) + */ static int zr36016_setup(struct videocodec *codec) { @@ -410,9 +388,7 @@ static const struct videocodec zr36016_codec = { /* others are not used */ }; -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ +/* HOOK IN DRIVER AS KERNEL MODULE */ int zr36016_init_module(void) { diff --git a/drivers/staging/media/zoran/zr36050.c b/drivers/staging/media/zoran/zr36050.c index 6a7ef28d996c..b07d7e5c1b4a 100644 --- a/drivers/staging/media/zoran/zr36050.c +++ b/drivers/staging/media/zoran/zr36050.c @@ -22,18 +22,20 @@ /* codec io API */ #include "videocodec.h" -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ +/* + * it doesn't make sense to have more than 20 or so, + * just to prevent some unwanted loops + */ #define MAX_CODECS 20 /* amount of chips attached via this driver */ static int zr36050_codecs; -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ +/* + * Local hardware I/O functions: + * + * read/write via codec layer (registers are located in the master device) + */ /* read and write functions */ static u8 zr36050_read(struct zr36050 *ptr, u16 reg) @@ -66,12 +68,6 @@ static void zr36050_write(struct zr36050 *ptr, u16 reg, u8 value) ptr->name); } -/* ========================================================================= - Local helper function: - - status read - ========================================================================= */ - /* status is kept in datastructure */ static u8 zr36050_read_status1(struct zr36050 *ptr) { @@ -81,12 +77,6 @@ static u8 zr36050_read_status1(struct zr36050 *ptr) return ptr->status1; } -/* ========================================================================= - Local helper function: - - scale factor read - ========================================================================= */ - /* scale factor is kept in datastructure */ static u16 zr36050_read_scalefactor(struct zr36050 *ptr) { @@ -98,11 +88,11 @@ static u16 zr36050_read_scalefactor(struct zr36050 *ptr) return ptr->scalefact; } -/* ========================================================================= - Local helper function: - - wait if codec is ready to proceed (end of processing) or time is over - ========================================================================= */ +/* + * Local helper function: + * + * wait if codec is ready to proceed (end of processing) or time is over + */ static void zr36050_wait_end(struct zr36050 *ptr) { @@ -120,11 +110,10 @@ static void zr36050_wait_end(struct zr36050 *ptr) } } -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from memory the SOF marker - ========================================================================= */ +/* + * Local helper function: basic test of "connectivity", writes/reads + * to/from memory the SOF marker + */ static int zr36050_basic_test(struct zr36050 *ptr) { @@ -160,11 +149,7 @@ static int zr36050_basic_test(struct zr36050 *ptr) return 0; /* looks good! */ } -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - ========================================================================= */ +/* Local helper function: simple loop for pushing the init datasets */ static int zr36050_pushit(struct zr36050 *ptr, u16 startreg, u16 len, const char *data) { @@ -179,16 +164,16 @@ static int zr36050_pushit(struct zr36050 *ptr, u16 startreg, u16 len, const char return i; } -/* ========================================================================= - Basic datasets: - - jpeg baseline setup data (you find it on lots places in internet, or just - extract it from any regular .jpg image...) - - Could be variable, but until it's not needed it they are just fixed to save - memory. Otherwise expand zr36050 structure with arrays, push the values to - it and initialize from there, as e.g. the linux zr36057/60 driver does it. - ========================================================================= */ +/* + * Basic datasets: + * + * jpeg baseline setup data (you find it on lots places in internet, or just + * extract it from any regular .jpg image...) + * + * Could be variable, but until it's not needed it they are just fixed to save + * memory. Otherwise expand zr36050 structure with arrays, push the values to + * it and initialize from there, as e.g. the linux zr36057/60 driver does it. + */ static const char zr36050_dqt[0x86] = { 0xff, 0xdb, //Marker: DQT @@ -281,18 +266,19 @@ static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; -/* ========================================================================= - Local helper functions: - - calculation and setup of parameter-dependent JPEG baseline segments - (needed for compression only) - ========================================================================= */ +/* + * Local helper functions: + * + * calculation and setup of parameter-dependent JPEG baseline segments + * (needed for compression only) + */ /* ------------------------------------------------------------------------- */ -/* SOF (start of frame) segment depends on width, height and sampling ratio - of each color component */ - +/* + * SOF (start of frame) segment depends on width, height and sampling ratio + * of each color component + */ static int zr36050_set_sof(struct zr36050 *ptr) { struct zoran *zr = videocodec_to_zoran(ptr->codec); @@ -313,7 +299,8 @@ static int zr36050_set_sof(struct zr36050 *ptr) sof_data[9] = NO_OF_COMPONENTS; for (i = 0; i < NO_OF_COMPONENTS; i++) { sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios + sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | + (ptr->v_samp_ratio[i]); // sampling ratios sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection } return zr36050_pushit(ptr, ZR050_SOF_IDX, @@ -322,8 +309,10 @@ static int zr36050_set_sof(struct zr36050 *ptr) /* ------------------------------------------------------------------------- */ -/* SOS (start of scan) segment depends on the used scan components - of each color component */ +/* + * SOS (start of scan) segment depends on the used scan components + * of each color component + */ static int zr36050_set_sos(struct zr36050 *ptr) { @@ -368,14 +357,14 @@ static int zr36050_set_dri(struct zr36050 *ptr) return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); } -/* ========================================================================= - Setup function: - - Setup compression/decompression of Zoran's JPEG processor - ( see also zoran 36050 manual ) - - ... sorry for the spaghetti code ... - ========================================================================= */ +/* + * Setup function: + * + * Setup compression/decompression of Zoran's JPEG processor + * ( see also zoran 36050 manual ) + * + * ... sorry for the spaghetti code ... + */ static void zr36050_init(struct zr36050 *ptr) { int sum = 0; @@ -411,8 +400,10 @@ static void zr36050_init(struct zr36050 *ptr) sum += zr36050_set_sos(ptr); sum += zr36050_set_dri(ptr); - /* setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) */ + /* + * setup the fixed jpeg tables - maybe variable, though - + * (see table init section above) + */ zrdev_dbg(zr, "%s: write DQT, DHT, APP\n", ptr->name); sum += zr36050_pushit(ptr, ZR050_DQT_IDX, sizeof(zr36050_dqt), zr36050_dqt); @@ -522,14 +513,16 @@ static void zr36050_init(struct zr36050 *ptr) zr36050_read(ptr, 0); } -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ +/* + * CODEC API FUNCTIONS + * + * this functions are accessed by the master via the API structure + */ -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ +/* + * set compression/expansion mode and launches codec - + * this should be the last call from the master before starting processing + */ static int zr36050_set_mode(struct videocodec *codec, int mode) { struct zr36050 *ptr = (struct zr36050 *)codec->data; @@ -558,9 +551,10 @@ static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm ptr->name, norm->h_start, norm->v_start, cap->x, cap->y, cap->width, cap->height, cap->decimation, cap->quality); - /* if () return -EINVAL; + /* * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... */ + * we allow invalid startx/y and norm for now ... + */ ptr->width = cap->width / (cap->decimation & 0xff); ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); @@ -579,8 +573,10 @@ static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm ptr->real_code_vol = size >> 3; /* in bytes */ - /* Set max_block_vol here (previously in zr36050_init, moved - * here for consistency with zr36060 code */ + /* + * Set max_block_vol here (previously in zr36050_init, moved + * here for consistency with zr36060 code + */ zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); return 0; @@ -637,8 +633,6 @@ static int zr36050_control(struct videocodec *codec, int type, int size, void *d if (size != sizeof(int)) return -EFAULT; ptr->total_code_vol = *ival; - /* (Kieran Morrissey) - * code copied from zr36060.c to ensure proper bitrate */ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; break; @@ -701,11 +695,7 @@ static int zr36050_control(struct videocodec *codec, int type, int size, void *d return size; } -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ +/* Exit and unregister function: Deinitializes Zoran's JPEG processor */ static int zr36050_unset(struct videocodec *codec) { @@ -727,14 +717,14 @@ static int zr36050_unset(struct videocodec *codec) return -EFAULT; } -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ +/* + * Setup and registry function: + * + * Initializes Zoran's JPEG processor + * + * Also sets pixel size, average code size, mode (compr./decompr.) + * (the given size is determined by the processor with the video interface) + */ static int zr36050_setup(struct videocodec *codec) { @@ -771,8 +761,8 @@ static int zr36050_setup(struct videocodec *codec) memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag - * (what is the difference?) */ + /* 0 or 1 - fixed file size flag (what is the difference?) */ + ptr->bitrate_ctrl = 0; ptr->mode = CODEC_DO_COMPRESSION; ptr->width = 384; ptr->height = 288; @@ -809,9 +799,7 @@ static const struct videocodec zr36050_codec = { // others are not used }; -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ +/* HOOK IN DRIVER AS KERNEL MODULE */ int zr36050_init_module(void) { diff --git a/drivers/staging/media/zoran/zr36057.h b/drivers/staging/media/zoran/zr36057.h index a2a75fd9f535..45d8afc62b37 100644 --- a/drivers/staging/media/zoran/zr36057.h +++ b/drivers/staging/media/zoran/zr36057.h @@ -11,117 +11,117 @@ /* Zoran ZR36057 registers */ #define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ -#define ZR36057_VFEHCR_HS_POL BIT(30) -#define ZR36057_VFEHCR_H_START 10 +#define ZR36057_VFEHCR_HS_POL BIT(30) +#define ZR36057_VFEHCR_H_START 10 #define ZR36057_VFEHCR_H_END 0 #define ZR36057_VFEHCR_HMASK 0x3ff #define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ -#define ZR36057_VFEVCR_VS_POL BIT(30) -#define ZR36057_VFEVCR_V_START 10 +#define ZR36057_VFEVCR_VS_POL BIT(30) +#define ZR36057_VFEVCR_V_START 10 #define ZR36057_VFEVCR_V_END 0 #define ZR36057_VFEVCR_VMASK 0x3ff #define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ -#define ZR36057_VFESPFR_EXT_FL BIT(26) -#define ZR36057_VFESPFR_TOP_FIELD BIT(25) -#define ZR36057_VFESPFR_VCLK_POL BIT(24) -#define ZR36057_VFESPFR_H_FILTER 21 -#define ZR36057_VFESPFR_HOR_DCM 14 -#define ZR36057_VFESPFR_VER_DCM 8 -#define ZR36057_VFESPFR_DISP_MODE 6 +#define ZR36057_VFESPFR_EXT_FL BIT(26) +#define ZR36057_VFESPFR_TOP_FIELD BIT(25) +#define ZR36057_VFESPFR_VCLK_POL BIT(24) +#define ZR36057_VFESPFR_H_FILTER 21 +#define ZR36057_VFESPFR_HOR_DCM 14 +#define ZR36057_VFESPFR_VER_DCM 8 +#define ZR36057_VFESPFR_DISP_MODE 6 #define ZR36057_VFESPFR_YUV422 (0 << 3) #define ZR36057_VFESPFR_RGB888 (1 << 3) #define ZR36057_VFESPFR_RGB565 (2 << 3) #define ZR36057_VFESPFR_RGB555 (3 << 3) -#define ZR36057_VFESPFR_ERR_DIF (1 << 2) -#define ZR36057_VFESPFR_PACK24 (1 << 1) -#define ZR36057_VFESPFR_LITTLE_ENDIAN (1 << 0) +#define ZR36057_VFESPFR_ERR_DIF BIT(2) +#define ZR36057_VFESPFR_PACK24 BIT(1) +#define ZR36057_VFESPFR_LITTLE_ENDIAN BIT(0) #define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ #define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ #define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ -#define ZR36057_VSSFGR_DISP_STRIDE 16 -#define ZR36057_VSSFGR_VID_OVF BIT(8) -#define ZR36057_VSSFGR_SNAP_SHOT BIT(1) -#define ZR36057_VSSFGR_FRAME_GRAB BIT(0) +#define ZR36057_VSSFGR_DISP_STRIDE 16 +#define ZR36057_VSSFGR_VID_OVF BIT(8) +#define ZR36057_VSSFGR_SNAP_SHOT BIT(1) +#define ZR36057_VSSFGR_FRAME_GRAB BIT(0) #define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ -#define ZR36057_VDCR_VID_EN BIT(31) -#define ZR36057_VDCR_MIN_PIX 24 -#define ZR36057_VDCR_TRITON BIT(24) -#define ZR36057_VDCR_VID_WIN_HT 12 -#define ZR36057_VDCR_VID_WIN_WID 0 +#define ZR36057_VDCR_VID_EN BIT(31) +#define ZR36057_VDCR_MIN_PIX 24 +#define ZR36057_VDCR_TRITON BIT(24) +#define ZR36057_VDCR_VID_WIN_HT 12 +#define ZR36057_VDCR_VID_WIN_WID 0 #define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ #define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ #define ZR36057_OCR 0x024 /* Overlay Control Register */ -#define ZR36057_OCR_OVL_ENABLE BIT(15) -#define ZR36057_OCR_MASK_STRIDE 0 +#define ZR36057_OCR_OVL_ENABLE BIT(15) +#define ZR36057_OCR_MASK_STRIDE 0 #define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ -#define ZR36057_SPGPPCR_SOFT_RESET BIT(24) +#define ZR36057_SPGPPCR_SOFT_RESET BIT(24) #define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ #define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ #define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ -#define ZR36057_MCTCR_COD_TIME BIT(30) -#define ZR36057_MCTCR_C_EMPTY BIT(29) -#define ZR36057_MCTCR_C_FLUSH BIT(28) +#define ZR36057_MCTCR_COD_TIME BIT(30) +#define ZR36057_MCTCR_C_EMPTY BIT(29) +#define ZR36057_MCTCR_C_FLUSH BIT(28) #define ZR36057_MCTCR_COD_GUEST_ID 20 #define ZR36057_MCTCR_COD_GUEST_REG 16 #define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ #define ZR36057_ISR 0x03c /* Interrupt Status Register */ -#define ZR36057_ISR_GIRQ1 BIT(30) -#define ZR36057_ISR_GIRQ0 BIT(29) -#define ZR36057_ISR_COD_REP_IRQ BIT(28) -#define ZR36057_ISR_JPEG_REP_IRQ BIT(27) +#define ZR36057_ISR_GIRQ1 BIT(30) +#define ZR36057_ISR_GIRQ0 BIT(29) +#define ZR36057_ISR_COD_REP_IRQ BIT(28) +#define ZR36057_ISR_JPEG_REP_IRQ BIT(27) #define ZR36057_ICR 0x040 /* Interrupt Control Register */ -#define ZR36057_ICR_GIRQ1 BIT(30) -#define ZR36057_ICR_GIRQ0 BIT(29) -#define ZR36057_ICR_COD_REP_IRQ BIT(28) -#define ZR36057_ICR_JPEG_REP_IRQ BIT(27) -#define ZR36057_ICR_INT_PIN_EN BIT(24) +#define ZR36057_ICR_GIRQ1 BIT(30) +#define ZR36057_ICR_GIRQ0 BIT(29) +#define ZR36057_ICR_COD_REP_IRQ BIT(28) +#define ZR36057_ICR_JPEG_REP_IRQ BIT(27) +#define ZR36057_ICR_INT_PIN_EN BIT(24) #define ZR36057_I2CBR 0x044 /* I2C Bus Register */ -#define ZR36057_I2CBR_SDA BIT(1) -#define ZR36057_I2CBR_SCL BIT(0) +#define ZR36057_I2CBR_SDA BIT(1) +#define ZR36057_I2CBR_SCL BIT(0) #define ZR36057_JMC 0x100 /* JPEG Mode and Control */ -#define ZR36057_JMC_JPG BIT(31) -#define ZR36057_JMC_JPG_EXP_MODE (0 << 29) -#define ZR36057_JMC_JPG_CMP_MODE BIT(29) -#define ZR36057_JMC_MJPG_EXP_MODE (2 << 29) -#define ZR36057_JMC_MJPG_CMP_MODE (3 << 29) -#define ZR36057_JMC_RTBUSY_FB BIT(6) -#define ZR36057_JMC_GO_EN BIT(5) -#define ZR36057_JMC_SYNC_MSTR BIT(4) -#define ZR36057_JMC_FLD_PER_BUFF BIT(3) -#define ZR36057_JMC_VFIFO_FB BIT(2) -#define ZR36057_JMC_CFIFO_FB BIT(1) -#define ZR36057_JMC_STLL_LIT_ENDIAN BIT(0) +#define ZR36057_JMC_JPG BIT(31) +#define ZR36057_JMC_JPG_EXP_MODE (0 << 29) +#define ZR36057_JMC_JPG_CMP_MODE BIT(29) +#define ZR36057_JMC_MJPG_EXP_MODE (2 << 29) +#define ZR36057_JMC_MJPG_CMP_MODE (3 << 29) +#define ZR36057_JMC_RTBUSY_FB BIT(6) +#define ZR36057_JMC_GO_EN BIT(5) +#define ZR36057_JMC_SYNC_MSTR BIT(4) +#define ZR36057_JMC_FLD_PER_BUFF BIT(3) +#define ZR36057_JMC_VFIFO_FB BIT(2) +#define ZR36057_JMC_CFIFO_FB BIT(1) +#define ZR36057_JMC_STLL_LIT_ENDIAN BIT(0) #define ZR36057_JPC 0x104 /* JPEG Process Control */ -#define ZR36057_JPC_P_RESET BIT(7) -#define ZR36057_JPC_COD_TRNS_EN BIT(5) -#define ZR36057_JPC_ACTIVE BIT(0) +#define ZR36057_JPC_P_RESET BIT(7) +#define ZR36057_JPC_COD_TRNS_EN BIT(5) +#define ZR36057_JPC_ACTIVE BIT(0) #define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ -#define ZR36057_VSP_VSYNC_SIZE 16 -#define ZR36057_VSP_FRM_TOT 0 +#define ZR36057_VSP_VSYNC_SIZE 16 +#define ZR36057_VSP_FRM_TOT 0 #define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ -#define ZR36057_HSP_HSYNC_START 16 -#define ZR36057_HSP_LINE_TOT 0 +#define ZR36057_HSP_HSYNC_START 16 +#define ZR36057_HSP_LINE_TOT 0 #define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ #define ZR36057_FHAP_NAX 16 @@ -132,22 +132,22 @@ #define ZR36057_FVAP_PAY 0 #define ZR36057_FPP 0x118 /* Field Process Parameters */ -#define ZR36057_FPP_ODD_EVEN BIT(0) +#define ZR36057_FPP_ODD_EVEN BIT(0) #define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ #define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ #define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ -#define ZR36057_JCGI_JPE_GUEST_ID 4 -#define ZR36057_JCGI_JPE_GUEST_REG 0 +#define ZR36057_JCGI_JPE_GUEST_ID 4 +#define ZR36057_JCGI_JPE_GUEST_REG 0 #define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ #define ZR36057_POR 0x200 /* Post Office Register */ -#define ZR36057_POR_PO_PEN BIT(25) -#define ZR36057_POR_PO_TIME BIT(24) -#define ZR36057_POR_PO_DIR BIT(23) +#define ZR36057_POR_PO_PEN BIT(25) +#define ZR36057_POR_PO_TIME BIT(24) +#define ZR36057_POR_PO_DIR BIT(23) #define ZR36057_STR 0x300 /* "Still" Transfer Register */ diff --git a/drivers/staging/media/zoran/zr36060.c b/drivers/staging/media/zoran/zr36060.c index 7798016f1f96..75fd167603dc 100644 --- a/drivers/staging/media/zoran/zr36060.c +++ b/drivers/staging/media/zoran/zr36060.c @@ -243,7 +243,10 @@ static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; -/* SOF (start of frame) segment depends on width, height and sampling ratio of each color component */ +/* + * SOF (start of frame) segment depends on width, height and sampling ratio + * of each color component + */ static int zr36060_set_sof(struct zr36060 *ptr) { struct zoran *zr = videocodec_to_zoran(ptr->codec); @@ -555,8 +558,6 @@ static int zr36060_set_video(struct videocodec *codec, const struct tvnorm *norm reg = 6 - 1; /* VsyncSize */ zr36060_write(ptr, ZR060_SGR_VSYNC, reg); - //reg = 30 - 1; /* HsyncSize */ -///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68); reg = 68; zr36060_write(ptr, ZR060_SGR_HSYNC, reg); diff --git a/drivers/staging/media/zoran/zr36060.h b/drivers/staging/media/zoran/zr36060.h index fbf5429534ac..75c88677a4bd 100644 --- a/drivers/staging/media/zoran/zr36060.h +++ b/drivers/staging/media/zoran/zr36060.h @@ -124,78 +124,78 @@ struct zr36060 { /* ZR36060 LOAD register bits */ -#define ZR060_LOAD_LOAD BIT(7) -#define ZR060_LOAD_SYNC_RST BIT(0) +#define ZR060_LOAD_LOAD BIT(7) +#define ZR060_LOAD_SYNC_RST BIT(0) /* ZR36060 Code FIFO Status register bits */ -#define ZR060_CFSR_BUSY BIT(7) -#define ZR060_CFSR_C_BUSY BIT(2) +#define ZR060_CFSR_BUSY BIT(7) +#define ZR060_CFSR_C_BUSY BIT(2) #define ZR060_CFSR_CFIFO (3 << 0) /* ZR36060 Code Interface register */ -#define ZR060_CIR_CODE16 BIT(7) -#define ZR060_CIR_ENDIAN BIT(6) -#define ZR060_CIR_CFIS BIT(2) -#define ZR060_CIR_CODE_MSTR BIT(0) +#define ZR060_CIR_CODE16 BIT(7) +#define ZR060_CIR_ENDIAN BIT(6) +#define ZR060_CIR_CFIS BIT(2) +#define ZR060_CIR_CODE_MSTR BIT(0) /* ZR36060 Codec Mode register */ -#define ZR060_CMR_COMP BIT(7) -#define ZR060_CMR_ATP BIT(6) -#define ZR060_CMR_PASS2 BIT(5) -#define ZR060_CMR_TLM BIT(4) -#define ZR060_CMR_BRB BIT(2) -#define ZR060_CMR_FSF BIT(1) +#define ZR060_CMR_COMP BIT(7) +#define ZR060_CMR_ATP BIT(6) +#define ZR060_CMR_PASS2 BIT(5) +#define ZR060_CMR_TLM BIT(4) +#define ZR060_CMR_BRB BIT(2) +#define ZR060_CMR_FSF BIT(1) /* ZR36060 Markers Enable register */ -#define ZR060_MER_APP BIT(7) -#define ZR060_MER_COM BIT(6) -#define ZR060_MER_DRI BIT(5) -#define ZR060_MER_DQT BIT(4) -#define ZR060_MER_DHT BIT(3) +#define ZR060_MER_APP BIT(7) +#define ZR060_MER_COM BIT(6) +#define ZR060_MER_DRI BIT(5) +#define ZR060_MER_DQT BIT(4) +#define ZR060_MER_DHT BIT(3) /* ZR36060 Interrupt Mask register */ -#define ZR060_IMR_EOAV BIT(3) -#define ZR060_IMR_EOI BIT(2) -#define ZR060_IMR_END BIT(1) -#define ZR060_IMR_DATA_ERR BIT(0) +#define ZR060_IMR_EOAV BIT(3) +#define ZR060_IMR_EOI BIT(2) +#define ZR060_IMR_END BIT(1) +#define ZR060_IMR_DATA_ERR BIT(0) /* ZR36060 Interrupt Status register */ #define ZR060_ISR_PRO_CNT (3 << 6) -#define ZR060_ISR_EOAV BIT(3) -#define ZR060_ISR_EOI BIT(2) -#define ZR060_ISR_END BIT(1) -#define ZR060_ISR_DATA_ERR BIT(0) +#define ZR060_ISR_EOAV BIT(3) +#define ZR060_ISR_EOI BIT(2) +#define ZR060_ISR_END BIT(1) +#define ZR060_ISR_DATA_ERR BIT(0) /* ZR36060 Video Control register */ -#define ZR060_VCR_VIDEO8 BIT(7) -#define ZR060_VCR_RANGE BIT(6) -#define ZR060_VCR_FI_DET BIT(3) -#define ZR060_VCR_FI_VEDGE BIT(2) -#define ZR060_VCR_FI_EXT BIT(1) -#define ZR060_VCR_SYNC_MSTR BIT(0) +#define ZR060_VCR_VIDEO8 BIT(7) +#define ZR060_VCR_RANGE BIT(6) +#define ZR060_VCR_FI_DET BIT(3) +#define ZR060_VCR_FI_VEDGE BIT(2) +#define ZR060_VCR_FI_EXT BIT(1) +#define ZR060_VCR_SYNC_MSTR BIT(0) /* ZR36060 Video Polarity register */ -#define ZR060_VPR_VCLK_POL BIT(7) -#define ZR060_VPR_P_VAL_POL BIT(6) -#define ZR060_VPR_POE_POL BIT(5) -#define ZR060_VPR_S_IMG_POL BIT(4) -#define ZR060_VPR_BL_POL BIT(3) -#define ZR060_VPR_FI_POL BIT(2) -#define ZR060_VPR_HS_POL BIT(1) -#define ZR060_VPR_VS_POL BIT(0) +#define ZR060_VPR_VCLK_POL BIT(7) +#define ZR060_VPR_P_VAL_POL BIT(6) +#define ZR060_VPR_POE_POL BIT(5) +#define ZR060_VPR_S_IMG_POL BIT(4) +#define ZR060_VPR_BL_POL BIT(3) +#define ZR060_VPR_FI_POL BIT(2) +#define ZR060_VPR_HS_POL BIT(1) +#define ZR060_VPR_VS_POL BIT(0) /* ZR36060 Scaling register */ -#define ZR060_SR_V_SCALE BIT(2) -#define ZR060_SR_H_SCALE2 BIT(0) +#define ZR060_SR_V_SCALE BIT(2) +#define ZR060_SR_H_SCALE2 BIT(0) #define ZR060_SR_H_SCALE4 (2 << 0) int zr36060_init_module(void); -- cgit v1.3-14-g43fede From 31b83c85cf1dab673a7d1270e742ccd68e9ca6b5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 13:54:43 +0200 Subject: media: zoran: the video device is video capture only, not M2M Set vfl_dir correctly as a capture device instead of as a M2M device. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/zoran_card.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c index 869aabde3bef..3975fc1b2ee3 100644 --- a/drivers/staging/media/zoran/zoran_card.c +++ b/drivers/staging/media/zoran/zoran_card.c @@ -876,12 +876,7 @@ static int zoran_init_video_device(struct zoran *zr, struct video_device *video_ video_dev->device_caps = V4L2_CAP_STREAMING | dir; strscpy(video_dev->name, ZR_DEVNAME(zr), sizeof(video_dev->name)); - /* - * It's not a mem2mem device, but you can both capture and output from one and the same - * device. This should really be split up into two device nodes, but that's a job for - * another day. - */ - video_dev->vfl_dir = VFL_DIR_M2M; + video_dev->vfl_dir = VFL_DIR_RX; zoran_queue_init(zr, &zr->vq, V4L2_BUF_TYPE_VIDEO_CAPTURE); err = video_register_device(video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); -- cgit v1.3-14-g43fede From 90f6b6affd51a29df41686be3bde30b97ec94fd2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 13:54:44 +0200 Subject: media: zoran: from VB2_READ/WRITE: read/write isn't supported The read/write file operations are not implemented, so no need to set VB2_READ and VB2_WRITE flags. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/zoran/zoran_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 212b6d1f16c9..fa672cc8bc67 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -964,7 +964,7 @@ int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) vq->dev = &zr->pci_dev->dev; vq->type = dir; - vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ | VB2_WRITE; + vq->io_modes = VB2_DMABUF | VB2_MMAP; vq->drv_priv = zr; vq->buf_struct_size = sizeof(struct zr_buffer); vq->ops = &zr_video_qops; -- cgit v1.3-14-g43fede From 2a0c28063de23646bb56152095ce73ea2284dc26 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 13:54:45 +0200 Subject: media: zoran: move to mainline The zoran driver can be moved back to mainline after it has been converted by Corentin Labbe to vb2. Note that the zoran driver no longer supports video output, but video capture is working fine now. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/Kconfig | 1 + drivers/media/pci/Makefile | 1 + drivers/media/pci/zoran/Kconfig | 74 ++ drivers/media/pci/zoran/Makefile | 7 + drivers/media/pci/zoran/videocodec.c | 278 ++++++ drivers/media/pci/zoran/videocodec.h | 325 +++++++ drivers/media/pci/zoran/zoran.h | 328 +++++++ drivers/media/pci/zoran/zoran_card.c | 1440 ++++++++++++++++++++++++++++ drivers/media/pci/zoran/zoran_card.h | 29 + drivers/media/pci/zoran/zoran_device.c | 956 ++++++++++++++++++ drivers/media/pci/zoran/zoran_device.h | 60 ++ drivers/media/pci/zoran/zoran_driver.c | 986 +++++++++++++++++++ drivers/media/pci/zoran/zr36016.c | 406 ++++++++ drivers/media/pci/zoran/zr36016.h | 94 ++ drivers/media/pci/zoran/zr36050.c | 817 ++++++++++++++++ drivers/media/pci/zoran/zr36050.h | 165 ++++ drivers/media/pci/zoran/zr36057.h | 154 +++ drivers/media/pci/zoran/zr36060.c | 870 +++++++++++++++++ drivers/media/pci/zoran/zr36060.h | 203 ++++ drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/zoran/Kconfig | 74 -- drivers/staging/media/zoran/Makefile | 7 - drivers/staging/media/zoran/TODO | 19 - drivers/staging/media/zoran/videocodec.c | 278 ------ drivers/staging/media/zoran/videocodec.h | 325 ------- drivers/staging/media/zoran/zoran.h | 328 ------- drivers/staging/media/zoran/zoran_card.c | 1440 ---------------------------- drivers/staging/media/zoran/zoran_card.h | 29 - drivers/staging/media/zoran/zoran_device.c | 956 ------------------ drivers/staging/media/zoran/zoran_device.h | 60 -- drivers/staging/media/zoran/zoran_driver.c | 986 ------------------- drivers/staging/media/zoran/zr36016.c | 406 -------- drivers/staging/media/zoran/zr36016.h | 94 -- drivers/staging/media/zoran/zr36050.c | 817 ---------------- drivers/staging/media/zoran/zr36050.h | 165 ---- drivers/staging/media/zoran/zr36057.h | 154 --- drivers/staging/media/zoran/zr36060.c | 870 ----------------- drivers/staging/media/zoran/zr36060.h | 203 ---- 39 files changed, 7194 insertions(+), 7214 deletions(-) create mode 100644 drivers/media/pci/zoran/Kconfig create mode 100644 drivers/media/pci/zoran/Makefile create mode 100644 drivers/media/pci/zoran/videocodec.c create mode 100644 drivers/media/pci/zoran/videocodec.h create mode 100644 drivers/media/pci/zoran/zoran.h create mode 100644 drivers/media/pci/zoran/zoran_card.c create mode 100644 drivers/media/pci/zoran/zoran_card.h create mode 100644 drivers/media/pci/zoran/zoran_device.c create mode 100644 drivers/media/pci/zoran/zoran_device.h create mode 100644 drivers/media/pci/zoran/zoran_driver.c create mode 100644 drivers/media/pci/zoran/zr36016.c create mode 100644 drivers/media/pci/zoran/zr36016.h create mode 100644 drivers/media/pci/zoran/zr36050.c create mode 100644 drivers/media/pci/zoran/zr36050.h create mode 100644 drivers/media/pci/zoran/zr36057.h create mode 100644 drivers/media/pci/zoran/zr36060.c create mode 100644 drivers/media/pci/zoran/zr36060.h delete mode 100644 drivers/staging/media/zoran/Kconfig delete mode 100644 drivers/staging/media/zoran/Makefile delete mode 100644 drivers/staging/media/zoran/TODO delete mode 100644 drivers/staging/media/zoran/videocodec.c delete mode 100644 drivers/staging/media/zoran/videocodec.h delete mode 100644 drivers/staging/media/zoran/zoran.h delete mode 100644 drivers/staging/media/zoran/zoran_card.c delete mode 100644 drivers/staging/media/zoran/zoran_card.h delete mode 100644 drivers/staging/media/zoran/zoran_device.c delete mode 100644 drivers/staging/media/zoran/zoran_device.h delete mode 100644 drivers/staging/media/zoran/zoran_driver.c delete mode 100644 drivers/staging/media/zoran/zr36016.c delete mode 100644 drivers/staging/media/zoran/zr36016.h delete mode 100644 drivers/staging/media/zoran/zr36050.c delete mode 100644 drivers/staging/media/zoran/zr36050.h delete mode 100644 drivers/staging/media/zoran/zr36057.h delete mode 100644 drivers/staging/media/zoran/zr36060.c delete mode 100644 drivers/staging/media/zoran/zr36060.h diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 1224d908713a..7a229dddadaf 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -19,6 +19,7 @@ source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" +source "drivers/media/pci/zoran/Kconfig" endif diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 551169a3e434..00d740b953d5 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_TW5864) += tw5864/ obj-$(CONFIG_VIDEO_TW686X) += tw686x/ obj-$(CONFIG_VIDEO_TW68) += tw68/ +obj-$(CONFIG_VIDEO_ZORAN) += zoran/ diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig new file mode 100644 index 000000000000..3fb3e27e04a8 --- /dev/null +++ b/drivers/media/pci/zoran/Kconfig @@ -0,0 +1,74 @@ +config VIDEO_ZORAN + tristate "Zoran ZR36057/36067 Video For Linux (Deprecated)" + depends on PCI && I2C_ALGOBIT && VIDEO_DEV + depends on !ALPHA + depends on DEBUG_FS + select VIDEOBUF2_DMA_CONTIG + select VIDEO_ADV7170 if VIDEO_ZORAN_LML33R10 + select VIDEO_ADV7175 if VIDEO_ZORAN_DC10 || VIDEO_ZORAN_DC30 + select VIDEO_BT819 if VIDEO_ZORAN_LML33 + select VIDEO_BT856 if VIDEO_ZORAN_LML33 || VIDEO_ZORAN_AVS6EYES + select VIDEO_BT866 if VIDEO_ZORAN_AVS6EYES + select VIDEO_KS0127 if VIDEO_ZORAN_AVS6EYES + select VIDEO_SAA711X if VIDEO_ZORAN_BUZ || VIDEO_ZORAN_LML33R10 + select VIDEO_SAA7110 if VIDEO_ZORAN_DC10 + select VIDEO_SAA7185 if VIDEO_ZORAN_BUZ + select VIDEO_VPX3220 if VIDEO_ZORAN_DC30 + help + Say Y for support for MJPEG capture cards based on the Zoran + 36057/36067 PCI controller chipset. This includes the Iomega + Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is + a driver homepage at . For + more information, check . + + To compile this driver as a module, choose M here: the + module will be called zr36067. + +config VIDEO_ZORAN_DC30 + bool "Pinnacle/Miro DC30(+) support" + depends on VIDEO_ZORAN + help + Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback + card. This also supports really old DC10 cards based on the + zr36050 MJPEG codec and zr36016 VFE. + +config VIDEO_ZORAN_ZR36060 + bool "Zoran ZR36060" + depends on VIDEO_ZORAN + help + Say Y to support Zoran boards based on 36060 chips. + This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33 + and 33 R10 and AverMedia 6 boards. + +config VIDEO_ZORAN_BUZ + bool "Iomega Buz support" + depends on VIDEO_ZORAN_ZR36060 + help + Support for the Iomega Buz MJPEG capture/playback card. + +config VIDEO_ZORAN_DC10 + bool "Pinnacle/Miro DC10(+) support" + depends on VIDEO_ZORAN_ZR36060 + help + Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback + card. + +config VIDEO_ZORAN_LML33 + bool "Linux Media Labs LML33 support" + depends on VIDEO_ZORAN_ZR36060 + help + Support for the Linux Media Labs LML33 MJPEG capture/playback + card. + +config VIDEO_ZORAN_LML33R10 + bool "Linux Media Labs LML33R10 support" + depends on VIDEO_ZORAN_ZR36060 + help + support for the Linux Media Labs LML33R10 MJPEG capture/playback + card. + +config VIDEO_ZORAN_AVS6EYES + bool "AverMedia 6 Eyes support" + depends on VIDEO_ZORAN_ZR36060 + help + Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/media/pci/zoran/Makefile b/drivers/media/pci/zoran/Makefile new file mode 100644 index 000000000000..9603bac0195c --- /dev/null +++ b/drivers/media/pci/zoran/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +zr36067-objs := zoran_device.o \ + zoran_driver.o zoran_card.o videocodec.o + +obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o +zr36067-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o +zr36067-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c new file mode 100644 index 000000000000..8efc5e06b0f7 --- /dev/null +++ b/drivers/media/pci/zoran/videocodec.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * VIDEO MOTION CODECs internal API for video devices + * + * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's + * bound to a master device. + * + * (c) 2002 Wolfgang Scherr + */ + +#include +#include +#include +#include +#include + +#include "videocodec.h" + +struct attached_list { + struct videocodec *codec; + struct attached_list *next; +}; + +struct codec_list { + const struct videocodec *codec; + int attached; + struct attached_list *list; + struct codec_list *next; +}; + +static struct codec_list *codeclist_top; + +/* ================================================= */ +/* function prototypes of the master/slave interface */ +/* ================================================= */ + +struct videocodec *videocodec_attach(struct videocodec_master *master) +{ + struct codec_list *h = codeclist_top; + struct zoran *zr; + struct attached_list *a, *ptr; + struct videocodec *codec; + int res; + + if (!master) { + pr_err("%s: no data\n", __func__); + return NULL; + } + + zr = videocodec_master_to_zoran(master); + + zrdev_dbg(zr, "%s: '%s', flags %lx, magic %lx\n", __func__, + master->name, master->flags, master->magic); + + if (!h) { + zrdev_err(zr, "%s: no device available\n", __func__); + return NULL; + } + + while (h) { + // attach only if the slave has at least the flags + // expected by the master + if ((master->flags & h->codec->flags) == master->flags) { + zrdev_dbg(zr, "%s: try '%s'\n", __func__, h->codec->name); + + codec = kmemdup(h->codec, sizeof(struct videocodec), GFP_KERNEL); + if (!codec) + goto out_kfree; + + res = strlen(codec->name); + snprintf(codec->name + res, sizeof(codec->name) - res, "[%d]", h->attached); + codec->master_data = master; + res = codec->setup(codec); + if (res == 0) { + zrdev_dbg(zr, "%s: '%s'\n", __func__, codec->name); + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + if (!ptr) + goto out_kfree; + ptr->codec = codec; + + a = h->list; + if (!a) { + h->list = ptr; + zrdev_dbg(zr, "videocodec: first element\n"); + } else { + while (a->next) + a = a->next; // find end + a->next = ptr; + zrdev_dbg(zr, "videocodec: in after '%s'\n", + h->codec->name); + } + + h->attached += 1; + return codec; + } + kfree(codec); + } + h = h->next; + } + + zrdev_err(zr, "%s: no codec found!\n", __func__); + return NULL; + + out_kfree: + kfree(codec); + return NULL; +} + +int videocodec_detach(struct videocodec *codec) +{ + struct codec_list *h = codeclist_top; + struct zoran *zr; + struct attached_list *a, *prev; + int res; + + if (!codec) { + pr_err("%s: no data\n", __func__); + return -EINVAL; + } + + zr = videocodec_to_zoran(codec); + + zrdev_dbg(zr, "%s: '%s', type: %x, flags %lx, magic %lx\n", __func__, + codec->name, codec->type, codec->flags, codec->magic); + + if (!h) { + zrdev_err(zr, "%s: no device left...\n", __func__); + return -ENXIO; + } + + while (h) { + a = h->list; + prev = NULL; + while (a) { + if (codec == a->codec) { + res = a->codec->unset(a->codec); + if (res >= 0) { + zrdev_dbg(zr, "%s: '%s'\n", __func__, + a->codec->name); + a->codec->master_data = NULL; + } else { + zrdev_err(zr, "%s: '%s'\n", __func__, a->codec->name); + a->codec->master_data = NULL; + } + if (!prev) { + h->list = a->next; + zrdev_dbg(zr, "videocodec: delete first\n"); + } else { + prev->next = a->next; + zrdev_dbg(zr, "videocodec: delete middle\n"); + } + kfree(a->codec); + kfree(a); + h->attached -= 1; + return 0; + } + prev = a; + a = a->next; + } + h = h->next; + } + + zrdev_err(zr, "%s: given codec not found!\n", __func__); + return -EINVAL; +} + +int videocodec_register(const struct videocodec *codec) +{ + struct codec_list *ptr, *h = codeclist_top; + struct zoran *zr; + + if (!codec) { + pr_err("%s: no data!\n", __func__); + return -EINVAL; + } + + zr = videocodec_to_zoran((struct videocodec *)codec); + + zrdev_dbg(zr, + "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", + codec->name, codec->type, codec->flags, codec->magic); + + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + ptr->codec = codec; + + if (!h) { + codeclist_top = ptr; + zrdev_dbg(zr, "videocodec: hooked in as first element\n"); + } else { + while (h->next) + h = h->next; // find the end + h->next = ptr; + zrdev_dbg(zr, "videocodec: hooked in after '%s'\n", + h->codec->name); + } + + return 0; +} + +int videocodec_unregister(const struct videocodec *codec) +{ + struct codec_list *prev = NULL, *h = codeclist_top; + struct zoran *zr; + + if (!codec) { + pr_err("%s: no data!\n", __func__); + return -EINVAL; + } + + zr = videocodec_to_zoran((struct videocodec *)codec); + + zrdev_dbg(zr, + "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", + codec->name, codec->type, codec->flags, codec->magic); + + if (!h) { + zrdev_err(zr, "%s: no device left...\n", __func__); + return -ENXIO; + } + + while (h) { + if (codec == h->codec) { + if (h->attached) { + zrdev_err(zr, "videocodec: '%s' is used\n", + h->codec->name); + return -EBUSY; + } + zrdev_dbg(zr, "videocodec: unregister '%s' is ok.\n", + h->codec->name); + if (!prev) { + codeclist_top = h->next; + zrdev_dbg(zr, + "videocodec: delete first element\n"); + } else { + prev->next = h->next; + zrdev_dbg(zr, + "videocodec: delete middle element\n"); + } + kfree(h); + return 0; + } + prev = h; + h = h->next; + } + + zrdev_err(zr, "%s: given codec not found!\n", __func__); + return -EINVAL; +} + +int videocodec_debugfs_show(struct seq_file *m) +{ + struct codec_list *h = codeclist_top; + struct attached_list *a; + + seq_puts(m, "lave or attached aster name type flags magic "); + seq_puts(m, "(connected as)\n"); + + while (h) { + seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", + h->codec->name, h->codec->type, + h->codec->flags, h->codec->magic); + a = h->list; + while (a) { + seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", + a->codec->master_data->name, + a->codec->master_data->type, + a->codec->master_data->flags, + a->codec->master_data->magic, + a->codec->name); + a = a->next; + } + h = h->next; + } + + return 0; +} diff --git a/drivers/media/pci/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h new file mode 100644 index 000000000000..6b69f69667f9 --- /dev/null +++ b/drivers/media/pci/zoran/videocodec.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * VIDEO MOTION CODECs internal API for video devices + * + * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's + * bound to a master device. + * + * (c) 2002 Wolfgang Scherr + */ + +/* =================== */ +/* general description */ +/* =================== */ + +/* + * Should ease the (re-)usage of drivers supporting cards with (different) + * video codecs. The codecs register to this module their functionality, + * and the processors (masters) can attach to them if they fit. + * + * The codecs are typically have a "strong" binding to their master - so I + * don't think it makes sense to have a full blown interfacing as with e.g. + * i2c. If you have an other opinion, let's discuss & implement it :-))) + * + * Usage: + * + * The slave has just to setup the videocodec structure and use two functions: + * videocodec_register(codecdata); + * videocodec_unregister(codecdata); + * The best is just calling them at module (de-)initialisation. + * + * The master sets up the structure videocodec_master and calls: + * codecdata=videocodec_attach(master_codecdata); + * videocodec_detach(codecdata); + * + * The slave is called during attach/detach via functions setup previously + * during register. At that time, the master_data pointer is set up + * and the slave can access any io registers of the master device (in the case + * the slave is bound to it). Otherwise it doesn't need this functions and + * therefor they may not be initialized. + * + * The other functions are just for convenience, as they are for sure used by + * most/all of the codecs. The last ones may be omitted, too. + * + * See the structure declaration below for more information and which data has + * to be set up for the master and the slave. + * + * ---------------------------------------------------------------------------- + * The master should have "knowledge" of the slave and vice versa. So the data + * structures sent to/from slave via set_data/get_data set_image/get_image are + * device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) + * ---------------------------------------------------------------------------- + */ + +/* ========================================== */ +/* description of the videocodec_io structure */ +/* ========================================== */ + +/* + * ==== master setup ==== + * name -> name of the device structure for reference and debugging + * master_data -> data ref. for the master (e.g. the zr36055,57,67) + * readreg -> ref. to read-fn from register (setup by master, used by slave) + * writereg -> ref. to write-fn to register (setup by master, used by slave) + * this two functions do the lowlevel I/O job + * + * ==== slave functionality setup ==== + * slave_data -> data ref. for the slave (e.g. the zr36050,60) + * check -> fn-ref. checks availability of an device, returns -EIO on failure or + * the type on success + * this makes espcecially sense if a driver module supports more than + * one codec which may be quite similar to access, nevertheless it + * is good for a first functionality check + * + * -- main functions you always need for compression/decompression -- + * + * set_mode -> this fn-ref. resets the entire codec, and sets up the mode + * with the last defined norm/size (or device default if not + * available) - it returns 0 if the mode is possible + * set_size -> this fn-ref. sets the norm and image size for + * compression/decompression (returns 0 on success) + * the norm param is defined in videodev2.h (V4L2_STD_*) + * + * additional setup may be available, too - but the codec should work with + * some default values even without this + * + * set_data -> sets device-specific data (tables, quality etc.) + * get_data -> query device-specific data (tables, quality etc.) + * + * if the device delivers interrupts, they may be setup/handled here + * setup_interrupt -> codec irq setup (not needed for 36050/60) + * handle_interrupt -> codec irq handling (not needed for 36050/60) + + * if the device delivers pictures, they may be handled here + * put_image -> puts image data to the codec (not needed for 36050/60) + * get_image -> gets image data from the codec (not needed for 36050/60) + * the calls include frame numbers and flags (even/odd/...) + * if needed and a flag which allows blocking until its ready + */ + +/* ============== */ +/* user interface */ +/* ============== */ + +/* + * Currently there is only a information display planned, as the layer + * is not visible for the user space at all. + * + * Information is available via procfs. The current entry is "/proc/videocodecs" + * but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. + * + * A example for such an output is: + * + * lave or attached aster name type flags magic (connected as) + * S zr36050 0002 0000d001 00000000 (TEMPLATE) + * M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) + * M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) + */ + +/* =============================================== */ +/* special defines for the videocodec_io structure */ +/* =============================================== */ + +#ifndef __LINUX_VIDEOCODEC_H +#define __LINUX_VIDEOCODEC_H + +#include +#include + +#define CODEC_DO_COMPRESSION 0 +#define CODEC_DO_EXPANSION 1 + +/* this are the current codec flags I think they are needed */ +/* -> type value in structure */ +#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec +#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec +#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec +#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec + // room for other types + +#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match +#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec +#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend +#define CODEC_FLAG_ENCODER 0x00004000L // compression capability +#define CODEC_FLAG_DECODER 0x00008000L // decompression capability +#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling +#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O + +/* a list of modes, some are just examples (is there any HW?) */ +#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG +#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG +#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 +#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 +#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 +#define CODEC_MODE_MSDIVX 0x0006 // MS DivX +#define CODEC_MODE_ODIVX 0x0007 // Open DivX +#define CODEC_MODE_WAVELET 0x0008 // Wavelet + +/* this are the current codec types I want to implement */ +/* -> type value in structure */ +#define CODEC_TYPE_NONE 0 +#define CODEC_TYPE_L64702 1 +#define CODEC_TYPE_ZR36050 2 +#define CODEC_TYPE_ZR36016 3 +#define CODEC_TYPE_ZR36060 4 + +/* the type of data may be enhanced by future implementations (data-fn.'s) */ +/* -> used in command */ +#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ +#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ +#define CODEC_G_CODEC_MODE 0x8001 +#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ +#define CODEC_G_VFE 0x8002 +#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ + +#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ +#define CODEC_G_JPEG_TDS_BYTE 0x8010 +#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ +#define CODEC_G_JPEG_SCALE 0x8011 +#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ +#define CODEC_G_JPEG_HDT_DATA 0x8018 +#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ +#define CODEC_G_JPEG_QDT_DATA 0x8019 +#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ +#define CODEC_G_JPEG_APP_DATA 0x801A +#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ +#define CODEC_G_JPEG_COM_DATA 0x801B + +#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ +#define CODEC_G_PRIVATE 0x9000 + +#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ + +/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ +/* -> used in get_image, put_image */ +#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ +#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ + +/* ========================= */ +/* the structures itself ... */ +/* ========================= */ + +struct vfe_polarity { + unsigned int vsync_pol:1; + unsigned int hsync_pol:1; + unsigned int field_pol:1; + unsigned int blank_pol:1; + unsigned int subimg_pol:1; + unsigned int poe_pol:1; + unsigned int pvalid_pol:1; + unsigned int vclk_pol:1; +}; + +struct vfe_settings { + __u32 x, y; /* Offsets into image */ + __u32 width, height; /* Area to capture */ + __u16 decimation; /* Decimation divider */ + __u16 flags; /* Flags for capture */ + __u16 quality; /* quality of the video */ +}; + +struct tvnorm { + u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; +}; + +struct jpeg_com_marker { + int len; /* number of usable bytes in data */ + char data[60]; +}; + +struct jpeg_app_marker { + int appn; /* number app segment */ + int len; /* number of usable bytes in data */ + char data[60]; +}; + +struct videocodec { + /* -- filled in by slave device during register -- */ + char name[32]; + unsigned long magic; /* may be used for client<->master attaching */ + unsigned long flags; /* functionality flags */ + unsigned int type; /* codec type */ + + /* -- these is filled in later during master device attach -- */ + + struct videocodec_master *master_data; + + /* -- these are filled in by the slave device during register -- */ + + void *data; /* private slave data */ + + /* attach/detach client functions (indirect call) */ + int (*setup)(struct videocodec *codec); + int (*unset)(struct videocodec *codec); + + /* main functions, every client needs them for sure! */ + // set compression or decompression (or freeze, stop, standby, etc) + int (*set_mode)(struct videocodec *codec, int mode); + // setup picture size and norm (for the codec's video frontend) + int (*set_video)(struct videocodec *codec, const struct tvnorm *norm, + struct vfe_settings *cap, struct vfe_polarity *pol); + // other control commands, also mmap setup etc. + int (*control)(struct videocodec *codec, int type, int size, void *data); + + /* additional setup/query/processing (may be NULL pointer) */ + // interrupt setup / handling (for irq's delivered by master) + int (*setup_interrupt)(struct videocodec *codec, long mode); + int (*handle_interrupt)(struct videocodec *codec, int source, long flag); + // picture interface (if any) + long (*put_image)(struct videocodec *codec, int tr_type, int block, + long *fr_num, long *flag, long size, void *buf); + long (*get_image)(struct videocodec *codec, int tr_type, int block, + long *fr_num, long *flag, long size, void *buf); +}; + +struct videocodec_master { + /* -- filled in by master device for registration -- */ + char name[32]; + unsigned long magic; /* may be used for client<->master attaching */ + unsigned long flags; /* functionality flags */ + unsigned int type; /* master type */ + + void *data; /* private master data */ + + __u32 (*readreg)(struct videocodec *codec, __u16 reg); + void (*writereg)(struct videocodec *codec, __u16 reg, __u32 value); +}; + +/* ================================================= */ +/* function prototypes of the master/slave interface */ +/* ================================================= */ + +/* attach and detach commands for the master */ +// * master structure needs to be kmalloc'ed before calling attach +// and free'd after calling detach +// * returns pointer on success, NULL on failure +struct videocodec *videocodec_attach(struct videocodec_master *master); +// * 0 on success, <0 (errno) on failure +int videocodec_detach(struct videocodec *codec); + +/* register and unregister commands for the slaves */ +// * 0 on success, <0 (errno) on failure +int videocodec_register(const struct videocodec *codec); +// * 0 on success, <0 (errno) on failure +int videocodec_unregister(const struct videocodec *codec); + +/* the other calls are directly done via the videocodec structure! */ + +int videocodec_debugfs_show(struct seq_file *m); + +#include "zoran.h" +static inline struct zoran *videocodec_master_to_zoran(struct videocodec_master *master) +{ + struct zoran *zr = master->data; + + return zr; +} + +static inline struct zoran *videocodec_to_zoran(struct videocodec *codec) +{ + struct videocodec_master *master = codec->master_data; + + return videocodec_master_to_zoran(master); +} + +#endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h new file mode 100644 index 000000000000..56340553b282 --- /dev/null +++ b/drivers/media/pci/zoran/zoran.h @@ -0,0 +1,328 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * zoran - Iomega Buz driver + * + * Copyright (C) 1999 Rainer Johanni + * + * based on + * + * zoran.0.0.3 Copyright (C) 1998 Dave Perks + * + * and + * + * bttv - Bt848 frame grabber driver + * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + * & Marcus Metzler (mocm@thp.uni-koeln.de) + */ + +#ifndef _BUZ_H_ +#define _BUZ_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZR_NORM_PAL 0 +#define ZR_NORM_NTSC 1 +#define ZR_NORM_SECAM 2 + +struct zr_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vbuf; + struct list_head queue; +}; + +static inline struct zr_buffer *vb2_to_zr_buffer(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + return container_of(vbuf, struct zr_buffer, vbuf); +} + +#define ZORAN_NAME "ZORAN" /* name of the device */ + +#define ZR_DEVNAME(zr) ((zr)->name) + +#define BUZ_MAX_WIDTH (zr->timing->wa) +#define BUZ_MAX_HEIGHT (zr->timing->ha) +#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ +#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ + +#define BUZ_NUM_STAT_COM 4 +#define BUZ_MASK_STAT_COM 3 + +#define BUZ_MAX_INPUT 16 + +#include "zr36057.h" + +enum card_type { + UNKNOWN = -1, + + /* Pinnacle/Miro */ + DC10_OLD, /* DC30 like */ + DC10_NEW, /* DC10_PLUS like */ + DC10_PLUS, + DC30, + DC30_PLUS, + + /* Linux Media Labs */ + LML33, + LML33R10, + + /* Iomega */ + BUZ, + + /* AverMedia */ + AVS6EYES, + + /* total number of cards */ + NUM_CARDS +}; + +enum zoran_codec_mode { + BUZ_MODE_IDLE, /* nothing going on */ + BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ + BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ + BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ + BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ +}; + +enum zoran_map_mode { + ZORAN_MAP_MODE_NONE, + ZORAN_MAP_MODE_RAW, + ZORAN_MAP_MODE_JPG_REC, + ZORAN_MAP_MODE_JPG_PLAY, +}; + +enum gpio_type { + ZR_GPIO_JPEG_SLEEP = 0, + ZR_GPIO_JPEG_RESET, + ZR_GPIO_JPEG_FRAME, + ZR_GPIO_VID_DIR, + ZR_GPIO_VID_EN, + ZR_GPIO_VID_RESET, + ZR_GPIO_CLK_SEL1, + ZR_GPIO_CLK_SEL2, + ZR_GPIO_MAX, +}; + +enum gpcs_type { + GPCS_JPEG_RESET = 0, + GPCS_JPEG_START, + GPCS_MAX, +}; + +struct zoran_format { + char *name; + __u32 fourcc; + int colorspace; + int depth; + __u32 flags; + __u32 vfespfr; +}; + +/* flags */ +#define ZORAN_FORMAT_COMPRESSED BIT(0) +#define ZORAN_FORMAT_OVERLAY BIT(1) +#define ZORAN_FORMAT_CAPTURE BIT(2) +#define ZORAN_FORMAT_PLAYBACK BIT(3) + +/* v4l-capture settings */ +struct zoran_v4l_settings { + int width, height, bytesperline; /* capture size */ + const struct zoran_format *format; /* capture format */ +}; + +/* jpg-capture/-playback settings */ +struct zoran_jpg_settings { + /* this bit is used to set everything to default */ + int decimation; + /* capture decimation settings (tmp_dcm=1 means both fields) */ + int hor_dcm, ver_dcm, tmp_dcm; + /* field-settings (odd_even=1 (+tmp_dcm=1) means top-field-first) */ + int field_per_buff, odd_even; + /* crop settings (subframe capture) */ + int img_x, img_y, img_width, img_height; + /* JPEG-specific capture settings */ + struct v4l2_jpegcompression jpg_comp; +}; + +struct zoran; + +/* zoran_fh contains per-open() settings */ +struct zoran_fh { + struct v4l2_fh fh; + struct zoran *zr; +}; + +struct card_info { + enum card_type type; + char name[32]; + const char *i2c_decoder; /* i2c decoder device */ + const unsigned short *addrs_decoder; + const char *i2c_encoder; /* i2c encoder device */ + const unsigned short *addrs_encoder; + u16 video_vfe, video_codec; /* videocodec types */ + u16 audio_chip; /* audio type */ + + int inputs; /* number of video inputs */ + struct input { + int muxsel; + char name[32]; + } input[BUZ_MAX_INPUT]; + + v4l2_std_id norms; + const struct tvnorm *tvn[3]; /* supported TV norms */ + + u32 jpeg_int; /* JPEG interrupt */ + u32 vsync_int; /* VSYNC interrupt */ + s8 gpio[ZR_GPIO_MAX]; + u8 gpcs[GPCS_MAX]; + + struct vfe_polarity vfe_pol; + u8 gpio_pol[ZR_GPIO_MAX]; + + /* is the /GWS line connected? */ + u8 gws_not_connected; + + /* avs6eyes mux setting */ + u8 input_mux; + + void (*init)(struct zoran *zr); +}; + +struct zoran { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct video_device *video_dev; + struct vb2_queue vq; + + struct i2c_adapter i2c_adapter; /* */ + struct i2c_algo_bit_data i2c_algo; /* */ + u32 i2cbr; + + struct v4l2_subdev *decoder; /* video decoder sub-device */ + struct v4l2_subdev *encoder; /* video encoder sub-device */ + + struct videocodec *codec; /* video codec */ + struct videocodec *vfe; /* video front end */ + + struct mutex lock; /* file ops serialize lock */ + + u8 initialized; /* flag if zoran has been correctly initialized */ + struct card_info card; + const struct tvnorm *timing; + + unsigned short id; /* number of this device */ + char name[32]; /* name of this device */ + struct pci_dev *pci_dev; /* PCI device */ + unsigned char revision; /* revision of zr36057 */ + unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ + + spinlock_t spinlock; /* Spinlock */ + + /* Video for Linux parameters */ + int input; /* card's norm and input */ + v4l2_std_id norm; + + /* Current buffer params */ + unsigned int buffer_size; + + struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ + + /* Buz MJPEG parameters */ + enum zoran_codec_mode codec_mode; /* status of codec */ + struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ + + /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ + /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ + /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ + unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ + unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ + unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ + unsigned long jpg_que_tail; /* Index of last buffer in queue */ + unsigned long jpg_seq_num; /* count of frames since grab/play started */ + unsigned long jpg_err_seq; /* last seq_num before error */ + unsigned long jpg_err_shift; + unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ + unsigned long vbseq; + + /* zr36057's code buffer table */ + /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + __le32 *stat_com; + + /* Additional stuff for testing */ + unsigned int ghost_int; + int intr_counter_GIRQ1; + int intr_counter_GIRQ0; + int intr_counter_cod_rep_irq; + int intr_counter_jpeg_rep_irq; + int field_counter; + int irq1_in; + int irq1_out; + int jpeg_in; + int jpeg_out; + int JPEG_0; + int JPEG_1; + int end_event_missed; + int jpeg_missed; + int jpeg_error; + int num_errors; + int jpeg_max_missed; + int jpeg_min_missed; + unsigned int prepared; + unsigned int queued; + + u32 last_isr; + unsigned long frame_num; + int running; + int buf_in_reserve; + + dma_addr_t p_sc; + __le32 *stat_comb; + dma_addr_t p_scb; + enum zoran_map_mode map_mode; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + struct zr_buffer *inuse[BUZ_NUM_STAT_COM * 2]; + struct dentry *dbgfs_dir; +}; + +static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct zoran, v4l2_dev); +} + +/* + * There was something called _ALPHA_BUZ that used the PCI address instead of + * the kernel iomapped address for btread/btwrite. + */ +#define btwrite(dat, adr) writel((dat), zr->zr36057_mem + (adr)) +#define btread(adr) readl(zr->zr36057_mem + (adr)) + +#define btand(dat, adr) btwrite((dat) & btread(adr), (adr)) +#define btor(dat, adr) btwrite((dat) | btread(adr), (adr)) +#define btaor(dat, mask, adr) btwrite((dat) | ((mask) & btread(adr)), (adr)) + +#endif + +/* + * Debugging macros + */ +#define zrdev_dbg(zr, format, args...) \ + pci_dbg((zr)->pci_dev, format, ##args) \ + +#define zrdev_err(zr, format, args...) \ + pci_err((zr)->pci_dev, format, ##args) \ + +#define zrdev_info(zr, format, args...) \ + pci_info((zr)->pci_dev, format, ##args) \ + +int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir); +void zoran_queue_exit(struct zoran *zr); +int zr_set_buf(struct zoran *zr); diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c new file mode 100644 index 000000000000..3975fc1b2ee3 --- /dev/null +++ b/drivers/media/pci/zoran/zoran_card.c @@ -0,0 +1,1440 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "videocodec.h" +#include "zoran.h" +#include "zoran_card.h" +#include "zoran_device.h" +#include "zr36016.h" +#include "zr36050.h" +#include "zr36060.h" + +extern const struct zoran_format zoran_formats[]; + +static int card[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "Card type"); + +/* Default input and video norm at startup of the driver. */ + +static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ +module_param(default_input, uint, 0444); +MODULE_PARM_DESC(default_input, + "Default input (0=Composite, 1=S-Video, 2=Internal)"); + +static int default_mux = 1; /* 6 Eyes input selection */ +module_param(default_mux, int, 0644); +MODULE_PARM_DESC(default_mux, + "Default 6 Eyes mux setting (Input selection)"); + +static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ +module_param(default_norm, int, 0444); +MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); + +/* /dev/videoN, -1 for autodetect */ +static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); + +/* 1=Pass through TV signal when device is not used */ +/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ +int pass_through; +module_param(pass_through, int, 0644); +MODULE_PARM_DESC(pass_through, + "Pass TV signal through to TV-out when idling"); + +int zr36067_debug = 1; +module_param_named(debug, zr36067_debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-5)"); + +#define ZORAN_VERSION "0.10.1" + +MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); +MODULE_AUTHOR("Serguei Miridonov"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ZORAN_VERSION); + +#define ZR_DEVICE(subven, subdev, data) { \ + .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ + .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } + +static const struct pci_device_id zr36067_pci_tbl[] = { + ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10_PLUS), + ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30_PLUS), + ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), + ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), + ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), + {0} +}; +MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); + +static unsigned int zoran_num; /* number of cards found */ + +/* videocodec bus functions ZR36060 */ +static u32 zr36060_read(struct videocodec *codec, u16 reg) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + __u32 data; + + if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || + post_office_write(zr, 0, 2, reg & 0xff)) + return -1; + + data = post_office_read(zr, 0, 3) & 0xff; + return data; +} + +static void zr36060_write(struct videocodec *codec, u16 reg, u32 val) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + + if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || + post_office_write(zr, 0, 2, reg & 0xff)) + return; + + post_office_write(zr, 0, 3, val & 0xff); +} + +/* videocodec bus functions ZR36050 */ +static u32 zr36050_read(struct videocodec *codec, u16 reg) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + __u32 data; + + if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES + return -1; + + data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read + return data; +} + +static void zr36050_write(struct videocodec *codec, u16 reg, u32 val) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + + if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES + return; + + post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data +} + +/* videocodec bus functions ZR36016 */ +static u32 zr36016_read(struct videocodec *codec, u16 reg) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + __u32 data; + + if (post_office_wait(zr)) + return -1; + + data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read + return data; +} + +/* hack for in zoran_device.c */ +void zr36016_write(struct videocodec *codec, u16 reg, u32 val) +{ + struct zoran *zr = (struct zoran *)codec->master_data->data; + + if (post_office_wait(zr)) + return; + + post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data +} + +/* + * Board specific information + */ + +static void dc10_init(struct zoran *zr) +{ + /* Pixel clock selection */ + GPIO(zr, 4, 0); + GPIO(zr, 5, 1); + /* Enable the video bus sync signals */ + GPIO(zr, 7, 0); +} + +static void dc10plus_init(struct zoran *zr) +{ +} + +static void buz_init(struct zoran *zr) +{ + /* some stuff from Iomega */ + pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); + pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); + pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); +} + +static void lml33_init(struct zoran *zr) +{ + GPIO(zr, 2, 1); // Set Composite input/output +} + +static void avs6eyes_init(struct zoran *zr) +{ + // AverMedia 6-Eyes original driver by Christer Weinigel + + // Lifted straight from Christer's old driver and + // modified slightly by Martin Samuelsson. + + int mux = default_mux; /* 1 = BT866, 7 = VID1 */ + + GPIO(zr, 4, 1); /* Bt866 SLEEP on */ + udelay(2); + + GPIO(zr, 0, 1); /* ZR36060 /RESET on */ + GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ + GPIO(zr, 2, mux & 1); /* MUX S0 */ + GPIO(zr, 3, 0); /* /FRAME on */ + GPIO(zr, 4, 0); /* Bt866 SLEEP off */ + GPIO(zr, 5, mux & 2); /* MUX S1 */ + GPIO(zr, 6, 0); /* ? */ + GPIO(zr, 7, mux & 4); /* MUX S2 */ +} + +static const char *codecid_to_modulename(u16 codecid) +{ + const char *name = NULL; + + switch (codecid) { + case CODEC_TYPE_ZR36060: + name = "zr36060"; + break; + case CODEC_TYPE_ZR36050: + name = "zr36050"; + break; + case CODEC_TYPE_ZR36016: + name = "zr36016"; + break; + } + + return name; +} + +static int codec_init(struct zoran *zr, u16 codecid) +{ + switch (codecid) { + case CODEC_TYPE_ZR36060: +#ifdef CONFIG_VIDEO_ZORAN_ZR36060 + return zr36060_init_module(); +#else + pci_err(zr->pci_dev, "ZR36060 support is not enabled\n"); + return -EINVAL; +#endif + break; + case CODEC_TYPE_ZR36050: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + return zr36050_init_module(); +#else + pci_err(zr->pci_dev, "ZR36050 support is not enabled\n"); + return -EINVAL; +#endif + break; + case CODEC_TYPE_ZR36016: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + return zr36016_init_module(); +#else + pci_err(zr->pci_dev, "ZR36016 support is not enabled\n"); + return -EINVAL; +#endif + break; + } + + pci_err(zr->pci_dev, "unknown codec id %x\n", codecid); + return -EINVAL; +} + +static void codec_exit(struct zoran *zr, u16 codecid) +{ + switch (codecid) { + case CODEC_TYPE_ZR36060: +#ifdef CONFIG_VIDEO_ZORAN_ZR36060 + zr36060_cleanup_module(); +#endif + break; + case CODEC_TYPE_ZR36050: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + zr36050_cleanup_module(); +#endif + break; + case CODEC_TYPE_ZR36016: +#ifdef CONFIG_VIDEO_ZORAN_DC30 + zr36016_cleanup_module(); +#endif + break; + } +} + +static int videocodec_init(struct zoran *zr) +{ + const char *codec_name, *vfe_name; + int result; + + codec_name = codecid_to_modulename(zr->card.video_codec); + if (codec_name) { + result = codec_init(zr, zr->card.video_codec); + if (result < 0) { + pci_err(zr->pci_dev, "failed to load video codec %s: %d\n", + codec_name, result); + return result; + } + } + vfe_name = codecid_to_modulename(zr->card.video_vfe); + if (vfe_name) { + result = codec_init(zr, zr->card.video_vfe); + if (result < 0) { + pci_err(zr->pci_dev, "failed to load video vfe %s: %d\n", + vfe_name, result); + if (codec_name) + codec_exit(zr, zr->card.video_codec); + return result; + } + } + return 0; +} + +static void videocodec_exit(struct zoran *zr) +{ + if (zr->card.video_codec != CODEC_TYPE_NONE) + codec_exit(zr, zr->card.video_codec); + if (zr->card.video_vfe != CODEC_TYPE_NONE) + codec_exit(zr, zr->card.video_vfe); +} + +static const struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; +static const struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; +static const struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; +static const struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; + +static const struct tvnorm f50ccir601_lml33 = { 864, 720, 75 + 34, 804, 625, 576, 18 }; +static const struct tvnorm f60ccir601_lml33 = { 858, 720, 57 + 34, 788, 525, 480, 16 }; + +/* The DC10 (57/16/50) uses VActive as HSync, so h_start must be 0 */ +static const struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; +static const struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; + +/* + * FIXME: I cannot swap U and V in saa7114, so i do one pixel left shift in zoran (75 -> 74) + * (Maxim Yevtyushkin ) + */ +static const struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74 + 54, 804, 625, 576, 18 }; +static const struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56 + 54, 788, 525, 480, 16 }; + +/* + * FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I copy Maxim's left + * shift hack for the 6 Eyes. + * + * Christer's driver used the unshifted norms, though... + * /Sam + */ +static const struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; +static const struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; + +static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; +static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; +static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; +static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; +static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; +static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; +static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; +static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; +static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; +static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; + +static struct card_info zoran_cards[NUM_CARDS] = { + { + .type = DC10_OLD, + .name = "DC10(old)", + .i2c_decoder = "vpx3220a", + .addrs_decoder = vpx3220_addrs, + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .input_mux = 0, + .init = &dc10_init, + }, { + .type = DC10_NEW, + .name = "DC10(new)", + .i2c_decoder = "saa7110", + .addrs_decoder = saa7110_addrs, + .i2c_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 3, + .input = { + { 0, "Composite" }, + { 7, "S-Video" }, + { 5, "Internal/comp" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50sqpixel, + &f60sqpixel, + &f50sqpixel}, + .jpeg_int = ZR36057_ISR_GIRQ0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { -1, 1}, + .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .input_mux = 0, + .init = &dc10plus_init, + }, { + .type = DC10_PLUS, + .name = "DC10_PLUS", + .i2c_decoder = "saa7110", + .addrs_decoder = saa7110_addrs, + .i2c_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 3, + .input = { + { 0, "Composite" }, + { 7, "S-Video" }, + { 5, "Internal/comp" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50sqpixel, + &f60sqpixel, + &f50sqpixel + }, + .jpeg_int = ZR36057_ISR_GIRQ0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { -1, 1 }, + .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .input_mux = 0, + .init = &dc10plus_init, + }, { + .type = DC30, + .name = "DC30", + .i2c_decoder = "vpx3220a", + .addrs_decoder = vpx3220_addrs, + .i2c_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .input_mux = 0, + .init = &dc10_init, + }, { + .type = DC30_PLUS, + .name = "DC30_PLUS", + .i2c_decoder = "vpx3220a", + .addrs_decoder = vpx3220_addrs, + .i2c_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .input_mux = 0, + .init = &dc10_init, + }, { + .type = LML33, + .name = "LML33", + .i2c_decoder = "bt819a", + .addrs_decoder = bt819_addrs, + .i2c_encoder = "bt856", + .addrs_encoder = bt856_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 0, "Composite" }, + { 7, "S-Video" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL, + .tvn = { + &f50ccir601_lml33, + &f60ccir601_lml33, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .input_mux = 0, + .init = &lml33_init, + }, { + .type = LML33R10, + .name = "LML33R10", + .i2c_decoder = "saa7114", + .addrs_decoder = saa7114_addrs, + .i2c_encoder = "adv7170", + .addrs_encoder = adv717x_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 0, "Composite" }, + { 7, "S-Video" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL, + .tvn = { + &f50ccir601_lm33r10, + &f60ccir601_lm33r10, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .input_mux = 0, + .init = &lml33_init, + }, { + .type = BUZ, + .name = "Buz", + .i2c_decoder = "saa7111", + .addrs_decoder = saa7111_addrs, + .i2c_encoder = "saa7185", + .addrs_encoder = saa7185_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 3, "Composite" }, + { 7, "S-Video" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .tvn = { + &f50ccir601, + &f60ccir601, + &f50ccir601 + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .input_mux = 0, + .init = &buz_init, + }, { + .type = AVS6EYES, + .name = "6-Eyes", + /* + * AverMedia chose not to brand the 6-Eyes. Thus it can't be + * autodetected, and requires card=x. + */ + .i2c_decoder = "ks0127", + .addrs_decoder = ks0127_addrs, + .i2c_encoder = "bt866", + .addrs_encoder = bt866_addrs, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 10, + .input = { + { 0, "Composite 1" }, + { 1, "Composite 2" }, + { 2, "Composite 3" }, + { 4, "Composite 4" }, + { 5, "Composite 5" }, + { 6, "Composite 6" }, + { 8, "S-Video 1" }, + { 9, "S-Video 2" }, + {10, "S-Video 3" }, + {15, "YCbCr" } + }, + .norms = V4L2_STD_NTSC | V4L2_STD_PAL, + .tvn = { + &f50ccir601_avs6eyes, + &f60ccir601_avs6eyes, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam + .gpcs = { 3, 1 }, // Validity unknown /Sam + .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam + .gws_not_connected = 1, + .input_mux = 1, + .init = &avs6eyes_init, + } + +}; + +/* + * I2C functions + */ +/* software I2C functions */ +static int zoran_i2c_getsda(void *data) +{ + struct zoran *zr = (struct zoran *)data; + + return (btread(ZR36057_I2CBR) >> 1) & 1; +} + +static int zoran_i2c_getscl(void *data) +{ + struct zoran *zr = (struct zoran *)data; + + return btread(ZR36057_I2CBR) & 1; +} + +static void zoran_i2c_setsda(void *data, int state) +{ + struct zoran *zr = (struct zoran *)data; + + if (state) + zr->i2cbr |= 2; + else + zr->i2cbr &= ~2; + btwrite(zr->i2cbr, ZR36057_I2CBR); +} + +static void zoran_i2c_setscl(void *data, int state) +{ + struct zoran *zr = (struct zoran *)data; + + if (state) + zr->i2cbr |= 1; + else + zr->i2cbr &= ~1; + btwrite(zr->i2cbr, ZR36057_I2CBR); +} + +static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { + .setsda = zoran_i2c_setsda, + .setscl = zoran_i2c_setscl, + .getsda = zoran_i2c_getsda, + .getscl = zoran_i2c_getscl, + .udelay = 10, + .timeout = 100, +}; + +static int zoran_register_i2c(struct zoran *zr) +{ + zr->i2c_algo = zoran_i2c_bit_data_template; + zr->i2c_algo.data = zr; + strscpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), + sizeof(zr->i2c_adapter.name)); + i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); + zr->i2c_adapter.algo_data = &zr->i2c_algo; + zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; + return i2c_bit_add_bus(&zr->i2c_adapter); +} + +static void zoran_unregister_i2c(struct zoran *zr) +{ + i2c_del_adapter(&zr->i2c_adapter); +} + +/* Check a zoran_params struct for correctness, insert default params */ +int zoran_check_jpg_settings(struct zoran *zr, + struct zoran_jpg_settings *settings, int try) +{ + int err = 0, err0 = 0; + + pci_dbg(zr->pci_dev, "%s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", + __func__, settings->decimation, settings->hor_dcm, + settings->ver_dcm, settings->tmp_dcm); + pci_dbg(zr->pci_dev, "%s - x: %d, y: %d, w: %d, y: %d\n", __func__, + settings->img_x, settings->img_y, + settings->img_width, settings->img_height); + /* Check decimation, set default values for decimation = 1, 2, 4 */ + switch (settings->decimation) { + case 1: + + settings->hor_dcm = 1; + settings->ver_dcm = 1; + settings->tmp_dcm = 1; + settings->field_per_buff = 2; + settings->img_x = 0; + settings->img_y = 0; + settings->img_width = BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 2: + + settings->hor_dcm = 2; + settings->ver_dcm = 1; + settings->tmp_dcm = 2; + settings->field_per_buff = 1; + settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings->img_y = 0; + settings->img_width = + (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 4: + + if (zr->card.type == DC10_NEW) { + pci_dbg(zr->pci_dev, + "%s - HDec by 4 is not supported on the DC10\n", + __func__); + err0++; + break; + } + + settings->hor_dcm = 4; + settings->ver_dcm = 2; + settings->tmp_dcm = 2; + settings->field_per_buff = 1; + settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings->img_y = 0; + settings->img_width = + (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 0: + + /* We have to check the data the user has set */ + + if (settings->hor_dcm != 1 && settings->hor_dcm != 2 && + (zr->card.type == DC10_NEW || settings->hor_dcm != 4)) { + settings->hor_dcm = clamp(settings->hor_dcm, 1, 2); + err0++; + } + if (settings->ver_dcm != 1 && settings->ver_dcm != 2) { + settings->ver_dcm = clamp(settings->ver_dcm, 1, 2); + err0++; + } + if (settings->tmp_dcm != 1 && settings->tmp_dcm != 2) { + settings->tmp_dcm = clamp(settings->tmp_dcm, 1, 2); + err0++; + } + if (settings->field_per_buff != 1 && + settings->field_per_buff != 2) { + settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); + err0++; + } + if (settings->img_x < 0) { + settings->img_x = 0; + err0++; + } + if (settings->img_y < 0) { + settings->img_y = 0; + err0++; + } + if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { + settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); + err0++; + } + if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { + settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); + err0++; + } + if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { + settings->img_x = BUZ_MAX_WIDTH - settings->img_width; + err0++; + } + if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { + settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; + err0++; + } + if (settings->img_width % (16 * settings->hor_dcm) != 0) { + settings->img_width -= settings->img_width % (16 * settings->hor_dcm); + if (settings->img_width == 0) + settings->img_width = 16 * settings->hor_dcm; + err0++; + } + if (settings->img_height % (8 * settings->ver_dcm) != 0) { + settings->img_height -= settings->img_height % (8 * settings->ver_dcm); + if (settings->img_height == 0) + settings->img_height = 8 * settings->ver_dcm; + err0++; + } + + if (!try && err0) { + pci_err(zr->pci_dev, "%s - error in params for decimation = 0\n", __func__); + err++; + } + break; + default: + pci_err(zr->pci_dev, "%s - decimation = %d, must be 0, 1, 2 or 4\n", + __func__, settings->decimation); + err++; + break; + } + + if (settings->jpg_comp.quality > 100) + settings->jpg_comp.quality = 100; + if (settings->jpg_comp.quality < 5) + settings->jpg_comp.quality = 5; + if (settings->jpg_comp.APPn < 0) + settings->jpg_comp.APPn = 0; + if (settings->jpg_comp.APPn > 15) + settings->jpg_comp.APPn = 15; + if (settings->jpg_comp.APP_len < 0) + settings->jpg_comp.APP_len = 0; + if (settings->jpg_comp.APP_len > 60) + settings->jpg_comp.APP_len = 60; + if (settings->jpg_comp.COM_len < 0) + settings->jpg_comp.COM_len = 0; + if (settings->jpg_comp.COM_len > 60) + settings->jpg_comp.COM_len = 60; + if (err) + return -EINVAL; + return 0; +} + +static int zoran_init_video_device(struct zoran *zr, struct video_device *video_dev, int dir) +{ + int err; + + /* Now add the template and register the device unit. */ + *video_dev = zoran_template; + video_dev->v4l2_dev = &zr->v4l2_dev; + video_dev->lock = &zr->lock; + video_dev->device_caps = V4L2_CAP_STREAMING | dir; + + strscpy(video_dev->name, ZR_DEVNAME(zr), sizeof(video_dev->name)); + video_dev->vfl_dir = VFL_DIR_RX; + zoran_queue_init(zr, &zr->vq, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + err = video_register_device(video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); + if (err < 0) + return err; + video_set_drvdata(video_dev, zr); + return 0; +} + +static void zoran_exit_video_devices(struct zoran *zr) +{ + video_unregister_device(zr->video_dev); + kfree(zr->video_dev); +} + +static int zoran_init_video_devices(struct zoran *zr) +{ + int err; + + zr->video_dev = video_device_alloc(); + if (!zr->video_dev) + return -ENOMEM; + + err = zoran_init_video_device(zr, zr->video_dev, V4L2_CAP_VIDEO_CAPTURE); + if (err) + kfree(zr->video_dev); + return err; +} + +/* + * v4l2_device_unregister() will care about removing zr->encoder/zr->decoder + * via v4l2_i2c_subdev_unregister() + */ +static int zoran_i2c_init(struct zoran *zr) +{ + int err; + + pci_info(zr->pci_dev, "Initializing i2c bus...\n"); + + err = zoran_register_i2c(zr); + if (err) { + pci_err(zr->pci_dev, "%s - cannot initialize i2c bus\n", __func__); + return err; + } + + zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, + zr->card.i2c_decoder, 0, + zr->card.addrs_decoder); + if (!zr->decoder) { + pci_err(zr->pci_dev, "Fail to get decoder %s\n", zr->card.i2c_decoder); + err = -EINVAL; + goto error_decoder; + } + + if (zr->card.i2c_encoder) { + zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, + zr->card.i2c_encoder, 0, + zr->card.addrs_encoder); + if (!zr->encoder) { + pci_err(zr->pci_dev, "Fail to get encoder %s\n", zr->card.i2c_encoder); + err = -EINVAL; + goto error_decoder; + } + } + return 0; + +error_decoder: + zoran_unregister_i2c(zr); + return err; +} + +static void zoran_i2c_exit(struct zoran *zr) +{ + zoran_unregister_i2c(zr); +} + +void zoran_open_init_params(struct zoran *zr) +{ + int i; + + zr->v4l_settings.width = 192; + zr->v4l_settings.height = 144; + zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ + zr->v4l_settings.bytesperline = zr->v4l_settings.width * + ((zr->v4l_settings.format->depth + 7) / 8); + + /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ + zr->jpg_settings.decimation = 1; + zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ + if (zr->card.type != BUZ) + zr->jpg_settings.odd_even = 1; + else + zr->jpg_settings.odd_even = 0; + zr->jpg_settings.jpg_comp.APPn = 0; + zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ + memset(zr->jpg_settings.jpg_comp.APP_data, 0, + sizeof(zr->jpg_settings.jpg_comp.APP_data)); + zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ + memset(zr->jpg_settings.jpg_comp.COM_data, 0, + sizeof(zr->jpg_settings.jpg_comp.COM_data)); + zr->jpg_settings.jpg_comp.jpeg_markers = + V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; + i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); + if (i) + pci_err(zr->pci_dev, "%s internal error\n", __func__); + + zr->buffer_size = zr->v4l_settings.bytesperline * zr->v4l_settings.height; + + clear_interrupt_counters(zr); +} + +static int zr36057_init(struct zoran *zr) +{ + int j, err; + + pci_info(zr->pci_dev, "initializing card[%d]\n", zr->id); + + /* Avoid nonsense settings from user for default input/norm */ + if (default_norm < 0 || default_norm > 2) + default_norm = 0; + if (default_norm == 0) { + zr->norm = V4L2_STD_PAL; + zr->timing = zr->card.tvn[ZR_NORM_PAL]; + } else if (default_norm == 1) { + zr->norm = V4L2_STD_NTSC; + zr->timing = zr->card.tvn[ZR_NORM_NTSC]; + } else { + zr->norm = V4L2_STD_SECAM; + zr->timing = zr->card.tvn[ZR_NORM_SECAM]; + } + if (!zr->timing) { + pci_warn(zr->pci_dev, + "%s - default TV standard not supported by hardware. PAL will be used.\n", + __func__); + zr->norm = V4L2_STD_PAL; + zr->timing = zr->card.tvn[ZR_NORM_PAL]; + } + + if (default_input > zr->card.inputs - 1) { + pci_warn(zr->pci_dev, "default_input value %d out of range (0-%d)\n", + default_input, zr->card.inputs - 1); + default_input = 0; + } + zr->input = default_input; + + /* default setup (will be repeated at every open) */ + zoran_open_init_params(zr); + + /* allocate memory *before* doing anything to the hardware in case allocation fails */ + zr->stat_com = dma_alloc_coherent(&zr->pci_dev->dev, + BUZ_NUM_STAT_COM * sizeof(u32), + &zr->p_sc, GFP_KERNEL); + if (!zr->stat_com) + return -ENOMEM; + + for (j = 0; j < BUZ_NUM_STAT_COM; j++) + zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ + + zr->stat_comb = dma_alloc_coherent(&zr->pci_dev->dev, + BUZ_NUM_STAT_COM * sizeof(u32) * 2, + &zr->p_scb, GFP_KERNEL); + if (!zr->stat_comb) { + err = -ENOMEM; + goto exit_statcom; + } + + err = zoran_init_video_devices(zr); + if (err) + goto exit_statcomb; + + zoran_init_hardware(zr); + if (!pass_through) { + decoder_call(zr, video, s_stream, 0); + encoder_call(zr, video, s_routing, 2, 0, 0); + } + + zr->initialized = 1; + return 0; + +exit_statcomb: + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, + zr->stat_comb, zr->p_scb); +exit_statcom: + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), + zr->stat_com, zr->p_sc); + return err; +} + +static void zoran_remove(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct zoran *zr = to_zoran(v4l2_dev); + + if (!zr->initialized) + goto exit_free; + + debugfs_remove_recursive(zr->dbgfs_dir); + + zoran_queue_exit(zr); + + /* unregister videocodec bus */ + if (zr->codec) + videocodec_detach(zr->codec); + if (zr->vfe) + videocodec_detach(zr->vfe); + videocodec_exit(zr); + + /* unregister i2c bus */ + zoran_i2c_exit(zr); + /* disable PCI bus-mastering */ + zoran_set_pci_master(zr, 0); + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + pci_free_irq(zr->pci_dev, 0, zr); + /* unmap and free memory */ + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), + zr->stat_com, zr->p_sc); + dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, + zr->stat_comb, zr->p_scb); + pci_release_regions(pdev); + pci_disable_device(zr->pci_dev); + zoran_exit_video_devices(zr); +exit_free: + v4l2_ctrl_handler_free(&zr->hdl); + v4l2_device_unregister(&zr->v4l2_dev); +} + +void zoran_vdev_release(struct video_device *vdev) +{ + kfree(vdev); +} + +static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr, + int type) +{ + struct videocodec_master *m = NULL; + + m = devm_kmalloc(&zr->pci_dev->dev, sizeof(*m), GFP_KERNEL); + if (!m) + return m; + + /* + * magic and type are unused for master struct. Makes sense only at codec structs. + * In the past, .type were initialized to the old V4L1 .hardware value, + * as VID_HARDWARE_ZR36067 + */ + m->magic = 0L; + m->type = 0; + + m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; + strscpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + m->data = zr; + + switch (type) { + case CODEC_TYPE_ZR36060: + m->readreg = zr36060_read; + m->writereg = zr36060_write; + m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; + break; + case CODEC_TYPE_ZR36050: + m->readreg = zr36050_read; + m->writereg = zr36050_write; + m->flags |= CODEC_FLAG_JPEG; + break; + case CODEC_TYPE_ZR36016: + m->readreg = zr36016_read; + m->writereg = zr36016_write; + m->flags |= CODEC_FLAG_VFE; + break; + } + + return m; +} + +static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct zoran *zr = to_zoran(sd->v4l2_dev); + + /* + * Bt819 needs to reset its FIFO buffer using #FRST pin and + * LML33 card uses GPIO(7) for that. + */ + if (cmd == BT819_FIFO_RESET_LOW) + GPIO(zr, 7, 0); + else if (cmd == BT819_FIFO_RESET_HIGH) + GPIO(zr, 7, 1); +} + +static int zoran_video_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct zoran *zr = container_of(ctrl->handler, struct zoran, hdl); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + zr->jpg_settings.jpg_comp.quality = ctrl->val; + return zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops zoran_video_ctrl_ops = { + .s_ctrl = zoran_video_set_ctrl, +}; + +static int zoran_debugfs_show(struct seq_file *seq, void *v) +{ + struct zoran *zr = seq->private; + + seq_printf(seq, "Running mode %x\n", zr->running); + seq_printf(seq, "Codec mode %x\n", zr->codec_mode); + seq_printf(seq, "Norm %llx\n", zr->norm); + seq_printf(seq, "Input %d\n", zr->input); + seq_printf(seq, "Buffersize %d\n", zr->buffer_size); + + seq_printf(seq, "V4L width %dx%d\n", zr->v4l_settings.width, zr->v4l_settings.height); + seq_printf(seq, "V4L bytesperline %d\n", zr->v4l_settings.bytesperline); + + seq_printf(seq, "JPG decimation %u\n", zr->jpg_settings.decimation); + seq_printf(seq, "JPG hor_dcm %u\n", zr->jpg_settings.hor_dcm); + seq_printf(seq, "JPG ver_dcm %u\n", zr->jpg_settings.ver_dcm); + seq_printf(seq, "JPG tmp_dcm %u\n", zr->jpg_settings.tmp_dcm); + seq_printf(seq, "JPG odd_even %u\n", zr->jpg_settings.odd_even); + seq_printf(seq, "JPG crop %dx%d %d %d\n", + zr->jpg_settings.img_x, + zr->jpg_settings.img_y, + zr->jpg_settings.img_width, + zr->jpg_settings.img_height); + + seq_printf(seq, "Prepared %u\n", zr->prepared); + seq_printf(seq, "Queued %u\n", zr->queued); + + videocodec_debugfs_show(seq); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(zoran_debugfs); + +/* + * Scan for a Buz card (actually for the PCI controller ZR36057), + * request the irq and map the io memory + */ +static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + unsigned char latency, need_latency; + struct zoran *zr; + int result; + struct videocodec_master *master_vfe = NULL; + struct videocodec_master *master_codec = NULL; + int card_num; + unsigned int nr; + int err; + + pci_info(pdev, "Zoran MJPEG board driver version %s\n", ZORAN_VERSION); + + /* some mainboards might not do PCI-PCI data transfer well */ + if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) + pci_warn(pdev, "%s: chipset does not support reliable PCI-PCI DMA\n", + ZORAN_NAME); + + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + return err; + err = vb2_dma_contig_set_max_seg_size(&pdev->dev, U32_MAX); + if (err) + return err; + + nr = zoran_num++; + if (nr >= BUZ_MAX) { + pci_err(pdev, "driver limited to %d card(s) maximum\n", BUZ_MAX); + return -ENOENT; + } + + zr = devm_kzalloc(&pdev->dev, sizeof(*zr), GFP_KERNEL); + if (!zr) + return -ENOMEM; + + zr->v4l2_dev.notify = zoran_subdev_notify; + if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) + goto zr_free_mem; + zr->pci_dev = pdev; + zr->id = nr; + snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); + if (v4l2_ctrl_handler_init(&zr->hdl, 10)) + goto zr_unreg; + zr->v4l2_dev.ctrl_handler = &zr->hdl; + v4l2_ctrl_new_std(&zr->hdl, &zoran_video_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, + 100, 1, 50); + spin_lock_init(&zr->spinlock); + mutex_init(&zr->lock); + if (pci_enable_device(pdev)) + goto zr_unreg; + zr->revision = zr->pci_dev->revision; + + pci_info(zr->pci_dev, "Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n", + zr->revision < 2 ? '5' : '6', zr->revision, + zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0)); + if (zr->revision >= 2) + pci_info(zr->pci_dev, "Subsystem vendor=0x%04x id=0x%04x\n", + zr->pci_dev->subsystem_vendor, zr->pci_dev->subsystem_device); + + /* Use auto-detected card type? */ + if (card[nr] == -1) { + if (zr->revision < 2) { + pci_err(pdev, "No card type specified, please use the card=X module parameter\n"); + pci_err(pdev, "It is not possible to auto-detect ZR36057 based cards\n"); + goto zr_unreg; + } + + card_num = ent->driver_data; + if (card_num >= NUM_CARDS) { + pci_err(pdev, "Unknown card, try specifying card=X module parameter\n"); + goto zr_unreg; + } + pci_info(zr->pci_dev, "%s() - card %s detected\n", __func__, + zoran_cards[card_num].name); + } else { + card_num = card[nr]; + if (card_num >= NUM_CARDS || card_num < 0) { + pci_err(pdev, "User specified card type %d out of range (0 .. %d)\n", + card_num, NUM_CARDS - 1); + goto zr_unreg; + } + } + + /* + * even though we make this a non pointer and thus + * theoretically allow for making changes to this struct + * on a per-individual card basis at runtime, this is + * strongly discouraged. This structure is intended to + * keep general card information, no settings or anything + */ + zr->card = zoran_cards[card_num]; + snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "%s[%u]", + zr->card.name, zr->id); + + err = pci_request_regions(pdev, ZR_DEVNAME(zr)); + if (err) + goto zr_unreg; + + zr->zr36057_mem = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!zr->zr36057_mem) { + pci_err(pdev, "%s() - ioremap failed\n", __func__); + goto zr_pci_release; + } + + result = pci_request_irq(pdev, 0, zoran_irq, NULL, zr, ZR_DEVNAME(zr)); + if (result < 0) { + if (result == -EINVAL) { + pci_err(pdev, "%s - bad IRQ number or handler\n", __func__); + } else if (result == -EBUSY) { + pci_err(pdev, "%s - IRQ %d busy, change your PnP config in BIOS\n", + __func__, zr->pci_dev->irq); + } else { + pci_err(pdev, "%s - cannot assign IRQ, error code %d\n", __func__, result); + } + goto zr_pci_release; + } + + /* set PCI latency timer */ + pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, + &latency); + need_latency = zr->revision > 1 ? 32 : 48; + if (latency != need_latency) { + pci_info(zr->pci_dev, "Changing PCI latency from %d to %d\n", + latency, need_latency); + pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); + } + + zr36057_restart(zr); + + err = zoran_i2c_init(zr); + if (err) + goto zr_free_irq; + + pci_info(zr->pci_dev, "Initializing videocodec bus...\n"); + err = videocodec_init(zr); + if (err) + goto zr_unreg_i2c; + + /* reset JPEG codec */ + jpeg_codec_sleep(zr, 1); + jpeg_codec_reset(zr); + /* video bus enabled */ + /* display codec revision */ + if (zr->card.video_codec != 0) { + master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); + if (!master_codec) + goto zr_unreg_videocodec; + zr->codec = videocodec_attach(master_codec); + if (!zr->codec) { + pci_err(pdev, "%s - no codec found\n", __func__); + goto zr_unreg_videocodec; + } + if (zr->codec->type != zr->card.video_codec) { + pci_err(pdev, "%s - wrong codec\n", __func__); + goto zr_unreg_videocodec; + } + } + if (zr->card.video_vfe != 0) { + master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe); + if (!master_vfe) + goto zr_detach_codec; + zr->vfe = videocodec_attach(master_vfe); + if (!zr->vfe) { + pci_err(pdev, "%s - no VFE found\n", __func__); + goto zr_detach_codec; + } + if (zr->vfe->type != zr->card.video_vfe) { + pci_err(pdev, "%s = wrong VFE\n", __func__); + goto zr_detach_vfe; + } + } + + /* take care of Natoma chipset and a revision 1 zr36057 */ + if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) + pci_info(zr->pci_dev, "ZR36057/Natoma bug, max. buffer size is 128K\n"); + + if (zr36057_init(zr) < 0) + goto zr_detach_vfe; + + zr->map_mode = ZORAN_MAP_MODE_RAW; + + zr->dbgfs_dir = debugfs_create_dir(ZR_DEVNAME(zr), NULL); + debugfs_create_file("debug", 0444, zr->dbgfs_dir, zr, + &zoran_debugfs_fops); + return 0; + +zr_detach_vfe: + videocodec_detach(zr->vfe); +zr_detach_codec: + videocodec_detach(zr->codec); +zr_unreg_videocodec: + videocodec_exit(zr); +zr_unreg_i2c: + zoran_i2c_exit(zr); +zr_free_irq: + btwrite(0, ZR36057_SPGPPCR); + pci_free_irq(zr->pci_dev, 0, zr); +zr_pci_release: + pci_release_regions(pdev); +zr_unreg: + v4l2_ctrl_handler_free(&zr->hdl); + v4l2_device_unregister(&zr->v4l2_dev); +zr_free_mem: + + return -ENODEV; +} + +static struct pci_driver zoran_driver = { + .name = "zr36067", + .id_table = zr36067_pci_tbl, + .probe = zoran_probe, + .remove = zoran_remove, +}; + +module_pci_driver(zoran_driver); diff --git a/drivers/media/pci/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h new file mode 100644 index 000000000000..518cb426b446 --- /dev/null +++ b/drivers/media/pci/zoran/zoran_card.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + */ + +#ifndef __ZORAN_CARD_H__ +#define __ZORAN_CARD_H__ + +extern int zr36067_debug; + +/* Anybody who uses more than four? */ +#define BUZ_MAX 4 + +extern const struct video_device zoran_template; + +int zoran_check_jpg_settings(struct zoran *zr, + struct zoran_jpg_settings *settings, int try); +void zoran_open_init_params(struct zoran *zr); +void zoran_vdev_release(struct video_device *vdev); + +void zr36016_write(struct videocodec *codec, u16 reg, u32 val); + +#endif /* __ZORAN_CARD_H__ */ diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c new file mode 100644 index 000000000000..31f049b55529 --- /dev/null +++ b/drivers/media/pci/zoran/zoran_device.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles device access (PCI/I2C/codec/...) + * + * Copyright (C) 2000 Serguei Miridonov + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "videocodec.h" +#include "zoran.h" +#include "zoran_device.h" +#include "zoran_card.h" + +#define IRQ_MASK (ZR36057_ISR_GIRQ0 | \ + ZR36057_ISR_GIRQ1 | \ + ZR36057_ISR_JPEG_REP_IRQ) + +static bool lml33dpath; /* default = 0 + * 1 will use digital path in capture + * mode instead of analog. It can be + * used for picture adjustments using + * tool like xawtv while watching image + * on TV monitor connected to the output. + * However, due to absence of 75 Ohm + * load on Bt819 input, there will be + * some image imperfections + */ + +module_param(lml33dpath, bool, 0644); +MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)"); + +/* + * initialize video front end + */ +static void zr36057_init_vfe(struct zoran *zr) +{ + u32 reg; + + reg = btread(ZR36057_VFESPFR); + reg |= ZR36057_VFESPFR_LITTLE_ENDIAN; + reg &= ~ZR36057_VFESPFR_VCLK_POL; + reg |= ZR36057_VFESPFR_EXT_FL; + reg |= ZR36057_VFESPFR_TOP_FIELD; + btwrite(reg, ZR36057_VFESPFR); + reg = btread(ZR36057_VDCR); + if (pci_pci_problems & PCIPCI_TRITON) + // || zr->revision < 1) // Revision 1 has also Triton support + reg &= ~ZR36057_VDCR_TRITON; + else + reg |= ZR36057_VDCR_TRITON; + btwrite(reg, ZR36057_VDCR); +} + +/* + * General Purpose I/O and Guest bus access + */ + +/* + * This is a bit tricky. When a board lacks a GPIO function, the corresponding + * GPIO bit number in the card_info structure is set to 0. + */ + +void GPIO(struct zoran *zr, int bit, unsigned int value) +{ + u32 reg; + u32 mask; + + /* Make sure the bit number is legal + * A bit number of -1 (lacking) gives a mask of 0, + * making it harmless + */ + mask = (1 << (24 + bit)) & 0xff000000; + reg = btread(ZR36057_GPPGCR1) & ~mask; + if (value) + reg |= mask; + + btwrite(reg, ZR36057_GPPGCR1); + udelay(1); +} + +/* + * Wait til post office is no longer busy + */ + +int post_office_wait(struct zoran *zr) +{ + u32 por; + + while ((por = btread(ZR36057_POR)) & ZR36057_POR_PO_PEN) { + /* wait for something to happen */ + /* TODO add timeout */ + } + if ((por & ZR36057_POR_PO_TIME) && !zr->card.gws_not_connected) { + /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ + pci_info(zr->pci_dev, "pop timeout %08x\n", por); + return -1; + } + + return 0; +} + +int post_office_write(struct zoran *zr, unsigned int guest, + unsigned int reg, unsigned int value) +{ + u32 por; + + por = + ZR36057_POR_PO_DIR | ZR36057_POR_PO_TIME | ((guest & 7) << 20) | + ((reg & 7) << 16) | (value & 0xFF); + btwrite(por, ZR36057_POR); + + return post_office_wait(zr); +} + +int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg) +{ + u32 por; + + por = ZR36057_POR_PO_TIME | ((guest & 7) << 20) | ((reg & 7) << 16); + btwrite(por, ZR36057_POR); + if (post_office_wait(zr) < 0) + return -1; + + return btread(ZR36057_POR) & 0xFF; +} + +/* + * JPEG Codec access + */ + +void jpeg_codec_sleep(struct zoran *zr, int sleep) +{ + GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); + if (!sleep) { + pci_dbg(zr->pci_dev, "%s() - wake GPIO=0x%08x\n", + __func__, btread(ZR36057_GPPGCR1)); + usleep_range(500, 1000); + } else { + pci_dbg(zr->pci_dev, "%s() - sleep GPIO=0x%08x\n", + __func__, btread(ZR36057_GPPGCR1)); + udelay(2); + } +} + +int jpeg_codec_reset(struct zoran *zr) +{ + /* Take the codec out of sleep */ + jpeg_codec_sleep(zr, 0); + + if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { + post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, + 0); + udelay(2); + } else { + GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0); + udelay(2); + GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1); + udelay(2); + } + + return 0; +} + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the ZR36057 manual in front of + * you [AC]. + */ +static void zr36057_adjust_vfe(struct zoran *zr, enum zoran_codec_mode mode) +{ + u32 reg; + + switch (mode) { + case BUZ_MODE_MOTION_DECOMPRESS: + btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if ((reg & (1 << 10)) && zr->card.type != LML33R10) + reg += ((1 << 10) | 1); + + btwrite(reg, ZR36057_VFEHCR); + break; + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_IDLE: + default: + if ((zr->norm & V4L2_STD_NTSC) || + (zr->card.type == LML33R10 && + (zr->norm & V4L2_STD_PAL))) + btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); + else + btor(ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if (!(reg & (1 << 10)) && zr->card.type != LML33R10) + reg -= ((1 << 10) | 1); + + btwrite(reg, ZR36057_VFEHCR); + break; + } +} + +/* + * set geometry + */ + +static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, + const struct zoran_format *format) +{ + const struct tvnorm *tvn; + unsigned int h_start, h_end, v_start, v_end; + unsigned int disp_mode; + unsigned int vid_win_wid, vid_win_ht; + unsigned int hcrop1, hcrop2, vcrop1, vcrop2; + unsigned int wa, we, ha, he; + unsigned int X, Y, hor_dcm, ver_dcm; + u32 reg; + + tvn = zr->timing; + + wa = tvn->wa; + ha = tvn->ha; + + pci_dbg(zr->pci_dev, "set_vfe() - width = %d, height = %d\n", video_width, video_height); + + if (video_width < BUZ_MIN_WIDTH || + video_height < BUZ_MIN_HEIGHT || + video_width > wa || video_height > ha) { + pci_err(zr->pci_dev, "set_vfe: w=%d h=%d not valid\n", video_width, video_height); + return; + } + + /**** zr36057 ****/ + + /* horizontal */ + vid_win_wid = video_width; + X = DIV_ROUND_UP(vid_win_wid * 64, tvn->wa); + we = (vid_win_wid * 64) / X; + hor_dcm = 64 - X; + hcrop1 = 2 * ((tvn->wa - we) / 4); + hcrop2 = tvn->wa - we - hcrop1; + h_start = tvn->h_start ? tvn->h_start : 1; + /* (Ronald) Original comment: + * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" + * this is false. It inverses chroma values on the LML33R10 (so Cr + * suddenly is shown as Cb and reverse, really cool effect if you + * want to see blue faces, not useful otherwise). So don't use |1. + * However, the DC10 has '0' as h_start, but does need |1, so we + * use a dirty check... + */ + h_end = h_start + tvn->wa - 1; + h_start += hcrop1; + h_end -= hcrop2; + reg = ((h_start & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_START) + | ((h_end & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_END); + if (zr->card.vfe_pol.hsync_pol) + reg |= ZR36057_VFEHCR_HS_POL; + btwrite(reg, ZR36057_VFEHCR); + + /* Vertical */ + disp_mode = !(video_height > BUZ_MAX_HEIGHT / 2); + vid_win_ht = disp_mode ? video_height : video_height / 2; + Y = DIV_ROUND_UP(vid_win_ht * 64 * 2, tvn->ha); + he = (vid_win_ht * 64) / Y; + ver_dcm = 64 - Y; + vcrop1 = (tvn->ha / 2 - he) / 2; + vcrop2 = tvn->ha / 2 - he - vcrop1; + v_start = tvn->v_start; + // FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP + v_end = v_start + tvn->ha / 2; // - 1; + v_start += vcrop1; + v_end -= vcrop2; + reg = ((v_start & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_START) + | ((v_end & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_END); + if (zr->card.vfe_pol.vsync_pol) + reg |= ZR36057_VFEVCR_VS_POL; + btwrite(reg, ZR36057_VFEVCR); + + /* scaler and pixel format */ + reg = 0; + reg |= (hor_dcm << ZR36057_VFESPFR_HOR_DCM); + reg |= (ver_dcm << ZR36057_VFESPFR_VER_DCM); + reg |= (disp_mode << ZR36057_VFESPFR_DISP_MODE); + /* + * RJ: I don't know, why the following has to be the opposite + * of the corresponding ZR36060 setting, but only this way + * we get the correct colors when uncompressing to the screen + */ + //reg |= ZR36057_VFESPFR_VCLK_POL; + /* RJ: Don't know if that is needed for NTSC also */ + if (!(zr->norm & V4L2_STD_NTSC)) + reg |= ZR36057_VFESPFR_EXT_FL; // NEEDED!!!!!!! Wolfgang + reg |= ZR36057_VFESPFR_TOP_FIELD; + if (hor_dcm >= 48) + reg |= 3 << ZR36057_VFESPFR_H_FILTER; /* 5 tap filter */ + else if (hor_dcm >= 32) + reg |= 2 << ZR36057_VFESPFR_H_FILTER; /* 4 tap filter */ + else if (hor_dcm >= 16) + reg |= 1 << ZR36057_VFESPFR_H_FILTER; /* 3 tap filter */ + + reg |= format->vfespfr; + btwrite(reg, ZR36057_VFESPFR); + + /* display configuration */ + reg = (16 << ZR36057_VDCR_MIN_PIX) + | (vid_win_ht << ZR36057_VDCR_VID_WIN_HT) + | (vid_win_wid << ZR36057_VDCR_VID_WIN_WID); + if (pci_pci_problems & PCIPCI_TRITON) + // || zr->revision < 1) // Revision 1 has also Triton support + reg &= ~ZR36057_VDCR_TRITON; + else + reg |= ZR36057_VDCR_TRITON; + btwrite(reg, ZR36057_VDCR); + + zr36057_adjust_vfe(zr, zr->codec_mode); +} + +/* Enable/Disable uncompressed memory grabbing of the 36057 */ +void zr36057_set_memgrab(struct zoran *zr, int mode) +{ + if (mode) { + /* We only check SnapShot and not FrameGrab here. SnapShot==1 + * means a capture is already in progress, but FrameGrab==1 + * doesn't necessary mean that. It's more correct to say a 1 + * to 0 transition indicates a capture completed. If a + * capture is pending when capturing is tuned off, FrameGrab + * will be stuck at 1 until capturing is turned back on. + */ + if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) + pci_warn(zr->pci_dev, "%s(1) with SnapShot on!?\n", __func__); + + /* switch on VSync interrupts */ + btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts + btor(zr->card.vsync_int, ZR36057_ICR); // SW + + /* enable SnapShot */ + btor(ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); + + /* Set zr36057 video front end and enable video */ + zr36057_set_vfe(zr, zr->v4l_settings.width, + zr->v4l_settings.height, + zr->v4l_settings.format); + } else { + /* switch off VSync interrupts */ + btand(~zr->card.vsync_int, ZR36057_ICR); // SW + + /* re-enable grabbing to screen if it was running */ + btand(~ZR36057_VDCR_VID_EN, ZR36057_VDCR); + btand(~ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); + } +} + +/***************************************************************************** + * * + * Set up the Buz-specific MJPEG part * + * * + *****************************************************************************/ + +static inline void set_frame(struct zoran *zr, int val) +{ + GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val); +} + +static void set_videobus_dir(struct zoran *zr, int val) +{ + switch (zr->card.type) { + case LML33: + case LML33R10: + if (!lml33dpath) + GPIO(zr, 5, val); + else + GPIO(zr, 5, 1); + break; + default: + GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR], + zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val); + break; + } +} + +static void init_jpeg_queue(struct zoran *zr) +{ + int i; + + /* re-initialize DMA ring stuff */ + zr->jpg_que_head = 0; + zr->jpg_dma_head = 0; + zr->jpg_dma_tail = 0; + zr->jpg_que_tail = 0; + zr->jpg_seq_num = 0; + zr->jpeg_error = 0; + zr->num_errors = 0; + zr->jpg_err_seq = 0; + zr->jpg_err_shift = 0; + zr->jpg_queued_num = 0; + for (i = 0; i < BUZ_NUM_STAT_COM; i++) + zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ +} + +static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + const struct tvnorm *tvn; + u32 reg; + + tvn = zr->timing; + + /* assert P_Reset, disable code transfer, deassert Active */ + btwrite(0, ZR36057_JPC); + + /* MJPEG compression mode */ + switch (mode) { + case BUZ_MODE_MOTION_COMPRESS: + default: + reg = ZR36057_JMC_MJPG_CMP_MODE; + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + reg = ZR36057_JMC_MJPG_EXP_MODE; + reg |= ZR36057_JMC_SYNC_MSTR; + /* RJ: The following is experimental - improves the output to screen */ + //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM + break; + + case BUZ_MODE_STILL_COMPRESS: + reg = ZR36057_JMC_JPG_CMP_MODE; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + reg = ZR36057_JMC_JPG_EXP_MODE; + break; + } + reg |= ZR36057_JMC_JPG; + if (zr->jpg_settings.field_per_buff == 1) + reg |= ZR36057_JMC_FLD_PER_BUFF; + btwrite(reg, ZR36057_JMC); + + /* vertical */ + btor(ZR36057_VFEVCR_VS_POL, ZR36057_VFEVCR); + reg = (6 << ZR36057_VSP_VSYNC_SIZE) | + (tvn->ht << ZR36057_VSP_FRM_TOT); + btwrite(reg, ZR36057_VSP); + reg = ((zr->jpg_settings.img_y + tvn->v_start) << ZR36057_FVAP_NAY) | + (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); + btwrite(reg, ZR36057_FVAP); + + /* horizontal */ + if (zr->card.vfe_pol.hsync_pol) + btor(ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); + else + btand(~ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); + reg = ((tvn->h_sync_start) << ZR36057_HSP_HSYNC_START) | + (tvn->wt << ZR36057_HSP_LINE_TOT); + btwrite(reg, ZR36057_HSP); + reg = ((zr->jpg_settings.img_x + + tvn->h_start + 4) << ZR36057_FHAP_NAX) | + (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); + btwrite(reg, ZR36057_FHAP); + + /* field process parameters */ + if (zr->jpg_settings.odd_even) + reg = ZR36057_FPP_ODD_EVEN; + else + reg = 0; + + btwrite(reg, ZR36057_FPP); + + /* Set proper VCLK Polarity, else colors will be wrong during playback */ + //btor(ZR36057_VFESPFR_VCLK_POL, ZR36057_VFESPFR); + + /* code base address */ + btwrite(zr->p_sc, ZR36057_JCBA); + + /* FIFO threshold (FIFO is 160. double words) */ + /* NOTE: decimal values here */ + switch (mode) { + case BUZ_MODE_STILL_COMPRESS: + case BUZ_MODE_MOTION_COMPRESS: + if (zr->card.type != BUZ) + reg = 140; + else + reg = 60; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + case BUZ_MODE_MOTION_DECOMPRESS: + reg = 20; + break; + + default: + reg = 80; + break; + } + btwrite(reg, ZR36057_JCFT); + zr36057_adjust_vfe(zr, mode); +} + +void clear_interrupt_counters(struct zoran *zr) +{ + zr->intr_counter_GIRQ1 = 0; + zr->intr_counter_GIRQ0 = 0; + zr->intr_counter_cod_rep_irq = 0; + zr->intr_counter_jpeg_rep_irq = 0; + zr->field_counter = 0; + zr->irq1_in = 0; + zr->irq1_out = 0; + zr->jpeg_in = 0; + zr->jpeg_out = 0; + zr->JPEG_0 = 0; + zr->JPEG_1 = 0; + zr->end_event_missed = 0; + zr->jpeg_missed = 0; + zr->jpeg_max_missed = 0; + zr->jpeg_min_missed = 0x7fffffff; +} + +static u32 count_reset_interrupt(struct zoran *zr) +{ + u32 isr; + + isr = btread(ZR36057_ISR) & 0x78000000; + if (isr) { + if (isr & ZR36057_ISR_GIRQ1) { + btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); + zr->intr_counter_GIRQ1++; + } + if (isr & ZR36057_ISR_GIRQ0) { + btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); + zr->intr_counter_GIRQ0++; + } + if (isr & ZR36057_ISR_COD_REP_IRQ) { + btwrite(ZR36057_ISR_COD_REP_IRQ, ZR36057_ISR); + zr->intr_counter_cod_rep_irq++; + } + if (isr & ZR36057_ISR_JPEG_REP_IRQ) { + btwrite(ZR36057_ISR_JPEG_REP_IRQ, ZR36057_ISR); + zr->intr_counter_jpeg_rep_irq++; + } + } + return isr; +} + +void jpeg_start(struct zoran *zr) +{ + int reg; + + zr->frame_num = 0; + + /* deassert P_reset, disable code transfer, deassert Active */ + btwrite(ZR36057_JPC_P_RESET, ZR36057_JPC); + /* stop flushing the internal code buffer */ + btand(~ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); + /* enable code transfer */ + btor(ZR36057_JPC_COD_TRNS_EN, ZR36057_JPC); + + /* clear IRQs */ + btwrite(IRQ_MASK, ZR36057_ISR); + /* enable the JPEG IRQs */ + btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ | ZR36057_ICR_INT_PIN_EN, + ZR36057_ICR); + + set_frame(zr, 0); // \FRAME + + /* set the JPEG codec guest ID */ + reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPE_GUEST_ID) | + (0 << ZR36057_JCGI_JPE_GUEST_REG); + btwrite(reg, ZR36057_JCGI); + + if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && + zr->card.video_codec == CODEC_TYPE_ZR36050) { + /* Enable processing on the ZR36016 */ + if (zr->vfe) + zr36016_write(zr->vfe, 0, 1); + + /* load the address of the GO register in the ZR36050 latch */ + post_office_write(zr, 0, 0, 0); + } + + /* assert Active */ + btor(ZR36057_JPC_ACTIVE, ZR36057_JPC); + + /* enable the Go generation */ + btor(ZR36057_JMC_GO_EN, ZR36057_JMC); + usleep_range(30, 100); + + set_frame(zr, 1); // /FRAME +} + +void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct vfe_settings cap; + int field_size = zr->buffer_size / zr->jpg_settings.field_per_buff; + + zr->codec_mode = mode; + + cap.x = zr->jpg_settings.img_x; + cap.y = zr->jpg_settings.img_y; + cap.width = zr->jpg_settings.img_width; + cap.height = zr->jpg_settings.img_height; + cap.decimation = + zr->jpg_settings.hor_dcm | (zr->jpg_settings.ver_dcm << 8); + cap.quality = zr->jpg_settings.jpg_comp.quality; + + switch (mode) { + case BUZ_MODE_MOTION_COMPRESS: { + struct jpeg_app_marker app; + struct jpeg_com_marker com; + + /* In motion compress mode, the decoder output must be enabled, and + * the video bus direction set to input. + */ + set_videobus_dir(zr, 0); + decoder_call(zr, video, s_stream, 1); + encoder_call(zr, video, s_routing, 0, 0, 0); + + /* Take the JPEG codec and the VFE out of sleep */ + jpeg_codec_sleep(zr, 0); + + /* set JPEG app/com marker */ + app.appn = zr->jpg_settings.jpg_comp.APPn; + app.len = zr->jpg_settings.jpg_comp.APP_len; + memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); + zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, + sizeof(struct jpeg_app_marker), &app); + + com.len = zr->jpg_settings.jpg_comp.COM_len; + memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); + zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, + sizeof(struct jpeg_com_marker), &com); + + /* Setup the JPEG codec */ + zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, + sizeof(int), &field_size); + zr->codec->set_video(zr->codec, zr->timing, &cap, + &zr->card.vfe_pol); + zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); + + /* Setup the VFE */ + if (zr->vfe) { + zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, + sizeof(int), &field_size); + zr->vfe->set_video(zr->vfe, zr->timing, &cap, + &zr->card.vfe_pol); + zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); + } + + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + pci_dbg(zr->pci_dev, "enable_jpg(MOTION_COMPRESS)\n"); + break; + } + + case BUZ_MODE_MOTION_DECOMPRESS: + /* In motion decompression mode, the decoder output must be disabled, and + * the video bus direction set to output. + */ + decoder_call(zr, video, s_stream, 0); + set_videobus_dir(zr, 1); + encoder_call(zr, video, s_routing, 1, 0, 0); + + /* Take the JPEG codec and the VFE out of sleep */ + jpeg_codec_sleep(zr, 0); + /* Setup the VFE */ + if (zr->vfe) { + zr->vfe->set_video(zr->vfe, zr->timing, &cap, + &zr->card.vfe_pol); + zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); + } + /* Setup the JPEG codec */ + zr->codec->set_video(zr->codec, zr->timing, &cap, + &zr->card.vfe_pol); + zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); + + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + pci_dbg(zr->pci_dev, "enable_jpg(MOTION_DECOMPRESS)\n"); + break; + + case BUZ_MODE_IDLE: + default: + /* shut down processing */ + btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ), + ZR36057_ICR); + btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ, + ZR36057_ISR); + btand(~ZR36057_JMC_GO_EN, ZR36057_JMC); // \Go_en + + msleep(50); + + set_videobus_dir(zr, 0); + set_frame(zr, 1); // /FRAME + btor(ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); // /CFlush + btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active + btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); + btand(~ZR36057_JMC_SYNC_MSTR, ZR36057_JMC); + jpeg_codec_reset(zr); + jpeg_codec_sleep(zr, 1); + zr36057_adjust_vfe(zr, mode); + + decoder_call(zr, video, s_stream, 1); + encoder_call(zr, video, s_routing, 0, 0, 0); + + pci_dbg(zr->pci_dev, "enable_jpg(IDLE)\n"); + break; + } +} + +/* when this is called the spinlock must be held */ +void zoran_feed_stat_com(struct zoran *zr) +{ + /* move frames from pending queue to DMA */ + + int i, max_stat_com; + struct zr_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr = 0; + unsigned long flags; + unsigned long payload; + + max_stat_com = + (zr->jpg_settings.tmp_dcm == + 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); + + spin_lock_irqsave(&zr->queued_bufs_lock, flags); + while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com) { + buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); + if (!buf) { + pci_err(zr->pci_dev, "No buffer available to queue\n"); + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + return; + } + list_del(&buf->queue); + zr->buf_in_reserve--; + vbuf = &buf->vbuf; + vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; + phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + payload = vb2_get_plane_payload(&vbuf->vb2_buf, 0); + if (payload == 0) + payload = zr->buffer_size; + if (zr->jpg_settings.tmp_dcm == 1) { + /* fill 1 stat_com entry */ + i = (zr->jpg_dma_head - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + if (!(zr->stat_com[i] & cpu_to_le32(1))) + break; + zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); + zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); + zr->inuse[i] = buf; + zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); + } else { + /* fill 2 stat_com entries */ + i = ((zr->jpg_dma_head - + zr->jpg_err_shift) & 1) * 2; + if (!(zr->stat_com[i] & cpu_to_le32(1))) + break; + zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); + zr->stat_com[i + 1] = cpu_to_le32(zr->p_scb + i * 2 * 4); + + zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); + zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); + + zr->inuse[i] = buf; + zr->inuse[i + 1] = NULL; + } + zr->jpg_dma_head++; + } + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_queued_num++; +} + +/* when this is called the spinlock must be held */ +static void zoran_reap_stat_com(struct zoran *zr) +{ + /* move frames from DMA queue to done queue */ + + int i; + u32 stat_com; + unsigned int seq; + unsigned int dif; + unsigned long flags; + struct zr_buffer *buf; + unsigned int size = 0; + u32 fcnt; + + /* + * In motion decompress we don't have a hardware frame counter, + * we just count the interrupts here + */ + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_seq_num++; + + spin_lock_irqsave(&zr->queued_bufs_lock, flags); + while (zr->jpg_dma_tail < zr->jpg_dma_head) { + if (zr->jpg_settings.tmp_dcm == 1) + i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; + + stat_com = le32_to_cpu(zr->stat_com[i]); + if ((stat_com & 1) == 0) { + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + return; + } + + fcnt = (stat_com & GENMASK(31, 24)) >> 24; + size = (stat_com & GENMASK(22, 1)) >> 1; + + buf = zr->inuse[i]; + if (!buf) { + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + pci_err(zr->pci_dev, "No buffer at slot %d\n", i); + return; + } + buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, size); + + /* update sequence number with the help of the counter in stat_com */ + seq = (fcnt + zr->jpg_err_seq) & 0xff; + dif = (seq - zr->jpg_seq_num) & 0xff; + zr->jpg_seq_num += dif; + } + buf->vbuf.sequence = zr->jpg_settings.tmp_dcm == + 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; + zr->inuse[i] = NULL; + if (zr->jpg_settings.tmp_dcm != 1) + buf->vbuf.field = zr->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + else + buf->vbuf.field = zr->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; + vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); + + zr->jpg_dma_tail++; + } + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); +} + +irqreturn_t zoran_irq(int irq, void *dev_id) +{ + struct zoran *zr = dev_id; + u32 stat, astat; + + stat = count_reset_interrupt(zr); + astat = stat & IRQ_MASK; + if (astat & zr->card.vsync_int) { + if (zr->running == ZORAN_MAP_MODE_RAW) { + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) == 0) + pci_warn(zr->pci_dev, "BuzIRQ with SnapShot off ???\n"); + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FRAME_GRAB) == 0) + zr_set_buf(zr); + return IRQ_HANDLED; + } + if (astat & ZR36057_ISR_JPEG_REP_IRQ) { + if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && + zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { + pci_err(zr->pci_dev, "JPG IRQ when not in good mode\n"); + return IRQ_HANDLED; + } + zr->frame_num++; + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + return IRQ_HANDLED; + } + /* unused interrupts */ + } + zr->ghost_int++; + return IRQ_HANDLED; +} + +void zoran_set_pci_master(struct zoran *zr, int set_master) +{ + if (set_master) { + pci_set_master(zr->pci_dev); + } else { + u16 command; + + pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); + } +} + +void zoran_init_hardware(struct zoran *zr) +{ + /* Enable bus-mastering */ + zoran_set_pci_master(zr, 1); + + /* Initialize the board */ + if (zr->card.init) + zr->card.init(zr); + + decoder_call(zr, core, init, 0); + decoder_call(zr, video, s_std, zr->norm); + decoder_call(zr, video, s_routing, + zr->card.input[zr->input].muxsel, 0, 0); + + encoder_call(zr, core, init, 0); + encoder_call(zr, video, s_std_output, zr->norm); + encoder_call(zr, video, s_routing, 0, 0, 0); + + /* toggle JPEG codec sleep to sync PLL */ + jpeg_codec_sleep(zr, 1); + jpeg_codec_sleep(zr, 0); + + /* + * set individual interrupt enables (without GIRQ1) + * but don't global enable until zoran_open() + */ + zr36057_init_vfe(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + + btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts +} + +void zr36057_restart(struct zoran *zr) +{ + btwrite(0, ZR36057_SPGPPCR); + usleep_range(1000, 2000); + btor(ZR36057_SPGPPCR_SOFT_RESET, ZR36057_SPGPPCR); + usleep_range(1000, 2000); + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + /* set up GPIO direction - all output */ + btwrite(ZR36057_SPGPPCR_SOFT_RESET | 0, ZR36057_SPGPPCR); + + /* set up GPIO pins and guest bus timing */ + btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); +} + diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h new file mode 100644 index 000000000000..34fd5cc914eb --- /dev/null +++ b/drivers/media/pci/zoran/zoran_device.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + */ + +#ifndef __ZORAN_DEVICE_H__ +#define __ZORAN_DEVICE_H__ + +/* general purpose I/O */ +void GPIO(struct zoran *zr, int bit, unsigned int value); + +/* codec (or actually: guest bus) access */ +int post_office_wait(struct zoran *zr); +int post_office_write(struct zoran *zr, unsigned int guest, unsigned int reg, + unsigned int value); +int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg); + +void jpeg_codec_sleep(struct zoran *zr, int sleep); +int jpeg_codec_reset(struct zoran *zr); + +/* zr360x7 access to raw capture */ +void zr36057_overlay(struct zoran *zr, int on); +void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); +void zr36057_set_memgrab(struct zoran *zr, int mode); +int wait_grab_pending(struct zoran *zr); + +/* interrupts */ +void print_interrupts(struct zoran *zr); +void clear_interrupt_counters(struct zoran *zr); +irqreturn_t zoran_irq(int irq, void *dev_id); + +/* JPEG codec access */ +void jpeg_start(struct zoran *zr); +void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode); +void zoran_feed_stat_com(struct zoran *zr); + +/* general */ +void zoran_set_pci_master(struct zoran *zr, int set_master); +void zoran_init_hardware(struct zoran *zr); +void zr36057_restart(struct zoran *zr); + +extern const struct zoran_format zoran_formats[]; + +extern int v4l_bufsize; +extern int jpg_bufsize; +extern int pass_through; + +/* i2c */ +#define decoder_call(zr, o, f, args...) \ + v4l2_subdev_call((zr)->decoder, o, f, ##args) +#define encoder_call(zr, o, f, args...) \ + v4l2_subdev_call((zr)->encoder, o, f, ##args) + +#endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c new file mode 100644 index 000000000000..fa672cc8bc67 --- /dev/null +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -0,0 +1,986 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * Copyright (C) 2000 Serguei Miridonov + * + * Changes for BUZ by Wolfgang Scherr + * + * Changes for DC10/DC30 by Laurent Pinchart + * + * Changes for LML33R10 by Maxim Yevtyushkin + * + * Changes for videodev2/v4l2 by Ronald Bultje + * + * Based on + * + * Miro DC10 driver + * Copyright (C) 1999 Wolfgang Scherr + * + * Iomega Buz driver version 1.0 + * Copyright (C) 1999 Rainer Johanni + * + * buz.0.0.3 + * Copyright (C) 1998 Dave Perks + * + * bttv - Bt848 frame grabber driver + * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + * & Marcus Metzler (mocm@thp.uni-koeln.de) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "videocodec.h" + +#include +#include + +#include +#include "zoran.h" +#include "zoran_device.h" +#include "zoran_card.h" + +const struct zoran_format zoran_formats[] = { + { + .name = "15-bit RGB LE", + .fourcc = V4L2_PIX_FMT_RGB555, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 15, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF | + ZR36057_VFESPFR_LITTLE_ENDIAN, + }, { + .name = "15-bit RGB BE", + .fourcc = V4L2_PIX_FMT_RGB555X, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 15, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF, + }, { + .name = "16-bit RGB LE", + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF | + ZR36057_VFESPFR_LITTLE_ENDIAN, + }, { + .name = "16-bit RGB BE", + .fourcc = V4L2_PIX_FMT_RGB565X, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF, + }, { + .name = "24-bit RGB", + .fourcc = V4L2_PIX_FMT_BGR24, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 24, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_PACK24, + }, { + .name = "32-bit RGB LE", + .fourcc = V4L2_PIX_FMT_BGR32, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 32, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_LITTLE_ENDIAN, + }, { + .name = "32-bit RGB BE", + .fourcc = V4L2_PIX_FMT_RGB32, + .colorspace = V4L2_COLORSPACE_SRGB, + .depth = 32, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_RGB888, + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_YUV422, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE, + .vfespfr = ZR36057_VFESPFR_YUV422 | ZR36057_VFESPFR_LITTLE_ENDIAN, + }, { + .name = "Hardware-encoded Motion-JPEG", + .fourcc = V4L2_PIX_FMT_MJPEG, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .depth = 0, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_PLAYBACK | + ZORAN_FORMAT_COMPRESSED, + } +}; + +#define NUM_FORMATS ARRAY_SIZE(zoran_formats) + + /* + * small helper function for calculating buffersizes for v4l2 + * we calculate the nearest higher power-of-two, which + * will be the recommended buffersize + */ +static __u32 zoran_v4l2_calc_bufsize(struct zoran_jpg_settings *settings) +{ + __u8 div = settings->ver_dcm * settings->hor_dcm * settings->tmp_dcm; + __u32 num = (1024 * 512) / (div); + __u32 result = 2; + + num--; + while (num) { + num >>= 1; + result <<= 1; + } + + if (result < 8192) + return 8192; + + return result; +} + +/* + * V4L Buffer grabbing + */ +static int zoran_v4l_set_format(struct zoran *zr, int width, int height, + const struct zoran_format *format) +{ + int bpp; + + /* Check size and format of the grab wanted */ + + if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || + height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { + pci_dbg(zr->pci_dev, "%s - wrong frame size (%dx%d)\n", __func__, width, height); + return -EINVAL; + } + + bpp = (format->depth + 7) / 8; + + zr->buffer_size = height * width * bpp; + + /* Check against available buffer size */ + if (height * width * bpp > zr->buffer_size) { + pci_dbg(zr->pci_dev, "%s - video buffer size (%d kB) is too small\n", + __func__, zr->buffer_size >> 10); + return -EINVAL; + } + + /* The video front end needs 4-byte alinged line sizes */ + + if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { + pci_dbg(zr->pci_dev, "%s - wrong frame alignment\n", __func__); + return -EINVAL; + } + + zr->v4l_settings.width = width; + zr->v4l_settings.height = height; + zr->v4l_settings.format = format; + zr->v4l_settings.bytesperline = bpp * zr->v4l_settings.width; + + return 0; +} + +static int zoran_set_norm(struct zoran *zr, v4l2_std_id norm) +{ + if (!(norm & zr->card.norms)) { + pci_dbg(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); + return -EINVAL; + } + + if (norm & V4L2_STD_SECAM) + zr->timing = zr->card.tvn[ZR_NORM_SECAM]; + else if (norm & V4L2_STD_NTSC) + zr->timing = zr->card.tvn[ZR_NORM_NTSC]; + else + zr->timing = zr->card.tvn[ZR_NORM_PAL]; + + decoder_call(zr, video, s_std, norm); + encoder_call(zr, video, s_std_output, norm); + + /* Make sure the changes come into effect */ + zr->norm = norm; + + return 0; +} + +static int zoran_set_input(struct zoran *zr, int input) +{ + if (input == zr->input) + return 0; + + if (input < 0 || input >= zr->card.inputs) { + pci_dbg(zr->pci_dev, "%s - unsupported input %d\n", __func__, input); + return -EINVAL; + } + + zr->input = input; + + decoder_call(zr, video, s_routing, zr->card.input[input].muxsel, 0, 0); + + return 0; +} + +/* + * ioctl routine + */ + +static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) +{ + struct zoran *zr = video_drvdata(file); + + strscpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); + strscpy(cap->driver, "zoran", sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(zr->pci_dev)); + return 0; +} + +static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) +{ + unsigned int num, i; + + if (fmt->index >= ARRAY_SIZE(zoran_formats)) + return -EINVAL; + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + for (num = i = 0; i < NUM_FORMATS; i++) { + if (zoran_formats[i].flags & flag && num++ == fmt->index) { + strscpy(fmt->description, zoran_formats[i].name, + sizeof(fmt->description)); + /* fmt struct pre-zeroed, so adding '\0' not needed */ + fmt->pixelformat = zoran_formats[i].fourcc; + if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + return 0; + } + } + return -EINVAL; +} + +static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_fmtdesc *f) +{ + struct zoran *zr = video_drvdata(file); + + return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); +} + +static int zoran_g_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + + fmt->fmt.pix.width = zr->jpg_settings.img_width / zr->jpg_settings.hor_dcm; + fmt->fmt.pix.height = zr->jpg_settings.img_height * 2 / + (zr->jpg_settings.ver_dcm * zr->jpg_settings.tmp_dcm); + fmt->fmt.pix.sizeimage = zr->buffer_size; + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + if (zr->jpg_settings.tmp_dcm == 1) + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + + if (zr->map_mode != ZORAN_MAP_MODE_RAW) + return zoran_g_fmt_vid_out(file, __fh, fmt); + fmt->fmt.pix.width = zr->v4l_settings.width; + fmt->fmt.pix.height = zr->v4l_settings.height; + fmt->fmt.pix.sizeimage = zr->buffer_size; + fmt->fmt.pix.pixelformat = zr->v4l_settings.format->fourcc; + fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; + fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; + if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = V4L2_FIELD_TOP; + return 0; +} + +static int zoran_try_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + struct zoran_jpg_settings settings; + int res = 0; + + if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + settings = zr->jpg_settings; + + /* we actually need to set 'real' parameters now */ + if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) + settings.tmp_dcm = 1; + else + settings.tmp_dcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) + settings.ver_dcm = 2; + else + settings.ver_dcm = 1; + if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) + settings.hor_dcm = 4; + else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) + settings.hor_dcm = 2; + else + settings.hor_dcm = 1; + if (settings.tmp_dcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; + + if (settings.hor_dcm > 1) { + settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + } else { + settings.img_x = 0; + settings.img_width = BUZ_MAX_WIDTH; + } + + /* check */ + res = zoran_check_jpg_settings(zr, &settings, 1); + if (res) + return res; + + /* tell the user what we actually did */ + fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; + fmt->fmt.pix.height = settings.img_height * 2 / + (settings.tmp_dcm * settings.ver_dcm); + if (settings.tmp_dcm == 1) + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + + fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return res; +} + +static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + int bpp; + int i; + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) + return zoran_try_fmt_vid_out(file, __fh, fmt); + + for (i = 0; i < NUM_FORMATS; i++) + if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) + break; + + if (i == NUM_FORMATS) { + /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ + return -EINVAL; + } + + fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; + fmt->fmt.pix.colorspace = zoran_formats[i].colorspace; + if (BUZ_MAX_HEIGHT < (fmt->fmt.pix.height * 2)) + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = V4L2_FIELD_TOP; + + bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); + v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, + bpp == 2 ? 1 : 2, + &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, + 0, 0); + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * bpp; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height; + return 0; +} + +static int zoran_s_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); + struct zoran_jpg_settings settings; + int res = 0; + + pci_dbg(zr->pci_dev, "size=%dx%d, fmt=0x%x (%4.4s)\n", + fmt->fmt.pix.width, fmt->fmt.pix.height, + fmt->fmt.pix.pixelformat, + (char *)&printformat); + if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (!fmt->fmt.pix.height || !fmt->fmt.pix.width) + return -EINVAL; + + settings = zr->jpg_settings; + + /* we actually need to set 'real' parameters now */ + if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) + settings.tmp_dcm = 1; + else + settings.tmp_dcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) + settings.ver_dcm = 2; + else + settings.ver_dcm = 1; + if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) + settings.hor_dcm = 4; + else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) + settings.hor_dcm = 2; + else + settings.hor_dcm = 1; + if (settings.tmp_dcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; + + if (settings.hor_dcm > 1) { + settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + } else { + settings.img_x = 0; + settings.img_width = BUZ_MAX_WIDTH; + } + + /* check */ + res = zoran_check_jpg_settings(zr, &settings, 0); + if (res) + return res; + + /* it's ok, so set them */ + zr->jpg_settings = settings; + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + zr->map_mode = ZORAN_MAP_MODE_JPG_REC; + else + zr->map_mode = ZORAN_MAP_MODE_JPG_PLAY; + + zr->buffer_size = zoran_v4l2_calc_bufsize(&zr->jpg_settings); + + /* tell the user what we actually did */ + fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; + fmt->fmt.pix.height = settings.img_height * 2 / + (settings.tmp_dcm * settings.ver_dcm); + if (settings.tmp_dcm == 1) + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = zr->buffer_size; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return res; +} + +static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran *zr = video_drvdata(file); + struct zoran_fh *fh = __fh; + int i; + int res = 0; + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) + return zoran_s_fmt_vid_out(file, fh, fmt); + + for (i = 0; i < NUM_FORMATS; i++) + if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) + break; + if (i == NUM_FORMATS) { + pci_dbg(zr->pci_dev, "VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", + fmt->fmt.pix.pixelformat); + /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ + return -EINVAL; + } + + fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; + if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) + fmt->fmt.pix.height = BUZ_MAX_HEIGHT; + if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) + fmt->fmt.pix.width = BUZ_MAX_WIDTH; + if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) + fmt->fmt.pix.height = BUZ_MIN_HEIGHT; + if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) + fmt->fmt.pix.width = BUZ_MIN_WIDTH; + + zr->map_mode = ZORAN_MAP_MODE_RAW; + + res = zoran_v4l_set_format(zr, fmt->fmt.pix.width, fmt->fmt.pix.height, + &zoran_formats[i]); + if (res) + return res; + + /* tell the user the results/missing stuff */ + fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; + fmt->fmt.pix.sizeimage = zr->buffer_size; + fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; + if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = V4L2_FIELD_TOP; + return res; +} + +static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) +{ + struct zoran *zr = video_drvdata(file); + + *std = zr->norm; + return 0; +} + +static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) +{ + struct zoran *zr = video_drvdata(file); + int res = 0; + + if (zr->norm == std) + return 0; + + if (zr->running != ZORAN_MAP_MODE_NONE) + return -EBUSY; + + res = zoran_set_norm(zr, std); + return res; +} + +static int zoran_enum_input(struct file *file, void *__fh, + struct v4l2_input *inp) +{ + struct zoran *zr = video_drvdata(file); + + if (inp->index >= zr->card.inputs) + return -EINVAL; + + strscpy(inp->name, zr->card.input[inp->index].name, sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + + /* Get status of video decoder */ + decoder_call(zr, video, g_input_status, &inp->status); + return 0; +} + +static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) +{ + struct zoran *zr = video_drvdata(file); + + *input = zr->input; + + return 0; +} + +static int zoran_s_input(struct file *file, void *__fh, unsigned int input) +{ + struct zoran *zr = video_drvdata(file); + int res; + + if (zr->running != ZORAN_MAP_MODE_NONE) + return -EBUSY; + + res = zoran_set_input(zr, input); + return res; +} + +/* cropping (sub-frame capture) */ +static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) +{ + struct zoran *zr = video_drvdata(file); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pci_dbg(zr->pci_dev, "%s invalid selection type combination\n", __func__); + return -EINVAL; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r.top = zr->jpg_settings.img_y; + sel->r.left = zr->jpg_settings.img_x; + sel->r.width = zr->jpg_settings.img_width; + sel->r.height = zr->jpg_settings.img_height; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = BUZ_MIN_WIDTH; + sel->r.height = BUZ_MIN_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = BUZ_MAX_WIDTH; + sel->r.height = BUZ_MAX_HEIGHT; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel) +{ + struct zoran *zr = video_drvdata(file); + struct zoran_jpg_settings settings; + int res; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!sel->r.width || !sel->r.height) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (zr->map_mode == ZORAN_MAP_MODE_RAW) { + pci_dbg(zr->pci_dev, "VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n"); + return -EINVAL; + } + + settings = zr->jpg_settings; + + /* move into a form that we understand */ + settings.img_x = sel->r.left; + settings.img_y = sel->r.top; + settings.img_width = sel->r.width; + settings.img_height = sel->r.height; + + /* check validity */ + res = zoran_check_jpg_settings(zr, &settings, 0); + if (res) + return res; + + /* accept */ + zr->jpg_settings = settings; + return res; +} + +/* + * Output is disabled temporarily + * Zoran is picky about jpeg data it accepts. At least it seems to unsupport COM and APPn. + * So until a way to filter data will be done, disable output. + */ +static const struct v4l2_ioctl_ops zoran_ioctl_ops = { + .vidioc_querycap = zoran_querycap, + .vidioc_s_selection = zoran_s_selection, + .vidioc_g_selection = zoran_g_selection, + .vidioc_enum_input = zoran_enum_input, + .vidioc_g_input = zoran_g_input, + .vidioc_s_input = zoran_s_input, + .vidioc_g_std = zoran_g_std, + .vidioc_s_std = zoran_s_std, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations zoran_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +const struct video_device zoran_template = { + .name = ZORAN_NAME, + .fops = &zoran_fops, + .ioctl_ops = &zoran_ioctl_ops, + .release = &zoran_vdev_release, + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, +}; + +static int zr_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct zoran *zr = vb2_get_drv_priv(vq); + unsigned int size = zr->buffer_size; + + pci_dbg(zr->pci_dev, "%s nbuf=%u nplanes=%u", __func__, *nbuffers, *nplanes); + + zr->buf_in_reserve = 0; + + if (*nbuffers < vq->min_buffers_needed) + *nbuffers = vq->min_buffers_needed; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + else + return 0; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static void zr_vb2_queue(struct vb2_buffer *vb) +{ + struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); + struct zr_buffer *buf = vb2_to_zr_buffer(vb); + unsigned long flags; + + spin_lock_irqsave(&zr->queued_bufs_lock, flags); + list_add_tail(&buf->queue, &zr->queued_bufs); + zr->buf_in_reserve++; + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + if (zr->running == ZORAN_MAP_MODE_JPG_REC) + zoran_feed_stat_com(zr); + zr->queued++; +} + +static int zr_vb2_prepare(struct vb2_buffer *vb) +{ + struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); + + if (vb2_plane_size(vb, 0) < zr->buffer_size) + return -EINVAL; + zr->prepared++; + + return 0; +} + +int zr_set_buf(struct zoran *zr) +{ + struct zr_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr; + unsigned long flags; + u32 reg; + + if (zr->running == ZORAN_MAP_MODE_NONE) + return 0; + + if (zr->inuse[0]) { + buf = zr->inuse[0]; + buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); + buf->vbuf.sequence = zr->vbseq++; + vbuf = &buf->vbuf; + + buf->vbuf.field = V4L2_FIELD_INTERLACED; + if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) + buf->vbuf.field = V4L2_FIELD_INTERLACED; + else + buf->vbuf.field = V4L2_FIELD_TOP; + vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, zr->buffer_size); + vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); + zr->inuse[0] = NULL; + } + + spin_lock_irqsave(&zr->queued_bufs_lock, flags); + if (list_empty(&zr->queued_bufs)) { + btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); + vb2_queue_error(zr->video_dev->queue); + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + return -EINVAL; + } + buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); + if (!buf) { + btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); + vb2_queue_error(zr->video_dev->queue); + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + return -EINVAL; + } + list_del(&buf->queue); + zr->buf_in_reserve--; + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + + vbuf = &buf->vbuf; + vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; + phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + + if (!phys_addr) + return -EINVAL; + + zr->inuse[0] = buf; + + reg = phys_addr; + btwrite(reg, ZR36057_VDTR); + if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->v4l_settings.bytesperline; + btwrite(reg, ZR36057_VDBR); + + reg = 0; + if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->v4l_settings.bytesperline; + reg = (reg << ZR36057_VSSFGR_DISP_STRIDE); + reg |= ZR36057_VSSFGR_VID_OVF; + reg |= ZR36057_VSSFGR_SNAP_SHOT; + reg |= ZR36057_VSSFGR_FRAME_GRAB; + btwrite(reg, ZR36057_VSSFGR); + + btor(ZR36057_VDCR_VID_EN, ZR36057_VDCR); + return 0; +} + +static int zr_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct zoran *zr = vq->drv_priv; + int j; + + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = cpu_to_le32(1); + zr->inuse[j] = NULL; + } + zr->vbseq = 0; + + if (zr->map_mode != ZORAN_MAP_MODE_RAW) { + pci_dbg(zr->pci_dev, "START JPG\n"); + zr36057_restart(zr); + zoran_init_hardware(zr); + if (zr->map_mode == ZORAN_MAP_MODE_JPG_REC) + zr36057_enable_jpg(zr, BUZ_MODE_MOTION_DECOMPRESS); + else + zr36057_enable_jpg(zr, BUZ_MODE_MOTION_COMPRESS); + zoran_feed_stat_com(zr); + jpeg_start(zr); + zr->running = zr->map_mode; + btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); + return 0; + } + + pci_dbg(zr->pci_dev, "START RAW\n"); + zr36057_restart(zr); + zoran_init_hardware(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr36057_set_memgrab(zr, 1); + zr->running = zr->map_mode; + btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); + return 0; +} + +static void zr_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct zoran *zr = vq->drv_priv; + struct zr_buffer *buf; + unsigned long flags; + int j; + + btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); + if (zr->map_mode != ZORAN_MAP_MODE_RAW) + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr36057_set_memgrab(zr, 0); + zr->running = ZORAN_MAP_MODE_NONE; + + zoran_set_pci_master(zr, 0); + + if (!pass_through) { /* Switch to color bar */ + decoder_call(zr, video, s_stream, 0); + encoder_call(zr, video, s_routing, 2, 0, 0); + } + + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = cpu_to_le32(1); + if (!zr->inuse[j]) + continue; + buf = zr->inuse[j]; + pci_dbg(zr->pci_dev, "%s clean buf %d\n", __func__, j); + vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); + zr->inuse[j] = NULL; + } + + spin_lock_irqsave(&zr->queued_bufs_lock, flags); + while (!list_empty(&zr->queued_bufs)) { + buf = list_entry(zr->queued_bufs.next, struct zr_buffer, queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); + zr->buf_in_reserve--; + } + spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); + if (zr->buf_in_reserve) + pci_dbg(zr->pci_dev, "Buffer remaining %d\n", zr->buf_in_reserve); + zr->map_mode = ZORAN_MAP_MODE_RAW; +} + +static const struct vb2_ops zr_video_qops = { + .queue_setup = zr_vb2_queue_setup, + .buf_queue = zr_vb2_queue, + .buf_prepare = zr_vb2_prepare, + .start_streaming = zr_vb2_start_streaming, + .stop_streaming = zr_vb2_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) +{ + int err; + + spin_lock_init(&zr->queued_bufs_lock); + INIT_LIST_HEAD(&zr->queued_bufs); + + vq->dev = &zr->pci_dev->dev; + vq->type = dir; + + vq->io_modes = VB2_DMABUF | VB2_MMAP; + vq->drv_priv = zr; + vq->buf_struct_size = sizeof(struct zr_buffer); + vq->ops = &zr_video_qops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->gfp_flags = GFP_DMA32; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->min_buffers_needed = 9; + vq->lock = &zr->lock; + err = vb2_queue_init(vq); + if (err) + return err; + zr->video_dev->queue = vq; + return 0; +} + +void zoran_queue_exit(struct zoran *zr) +{ + vb2_queue_release(zr->video_dev->queue); +} diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c new file mode 100644 index 000000000000..4b328ad6083f --- /dev/null +++ b/drivers/media/pci/zoran/zr36016.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran ZR36016 basic configuration functions + * + * Copyright (C) 2001 Wolfgang Scherr + */ + +#include +#include +#include + +/* headerfile of this module */ +#include "zr36016.h" + +/* codec io API */ +#include "videocodec.h" + +/* + * it doesn't make sense to have more than 20 or so, + * just to prevent some unwanted loops + */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36016_codecs; + +/* + * Local hardware I/O functions: read/write via codec layer + * (registers are located in the master device) + */ + +/* read and write functions */ +static u8 zr36016_read(struct zr36016 *ptr, u16 reg) +{ + u8 value = 0; + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + /* just in case something is wrong... */ + if (ptr->codec->master_data->readreg) + value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; + else + zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); + + zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); + + return value; +} + +static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) + ptr->codec->master_data->writereg(ptr->codec, reg, value); + else + zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); +} + +/* + * indirect read and write functions + * + * the 016 supports auto-addr-increment, but + * writing it all time cost not much and is safer... + */ +static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) +{ + u8 value = 0; + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + /* just in case something is wrong... */ + if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) { + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); + value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; + } else { + zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name); + } + + zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n", + ptr->name, reg, value); + return value; +} + +static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, + value, reg); + + /* just in case something is wrong... */ + if (ptr->codec->master_data->writereg) { + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); + ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); + } else { + zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name); + } +} + +/* Local helper function: version read */ + +/* version kept in datastructure */ +static u8 zr36016_read_version(struct zr36016 *ptr) +{ + ptr->version = zr36016_read(ptr, 0) >> 4; + return ptr->version; +} + +/* + * Local helper function: basic test of "connectivity", writes/reads + * to/from PAX-Lo register + */ + +static int zr36016_basic_test(struct zr36016 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) { + int i; + + zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); + zrdev_dbg(zr, "%s: registers: ", ptr->name); + for (i = 0; i <= 0x0b; i++) + zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i)); + zrdev_dbg(zr, "\n"); + } + // for testing just write 0, then the default value to a register and read + // it back in both cases + zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); + if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { + zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); + return -ENXIO; + } + zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); + if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { + zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); + return -ENXIO; + } + // we allow version numbers from 0-3, should be enough, though + zr36016_read_version(ptr); + if (ptr->version & 0x0c) { + zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name, + ptr->version); + return -ENXIO; + } + + return 0; /* looks good! */ +} + +/* Basic datasets & init */ + +static void zr36016_init(struct zr36016 *ptr) +{ + // stop any processing + zr36016_write(ptr, ZR016_GOSTOP, 0); + + // mode setup (yuv422 in and out, compression/expansuon due to mode) + zr36016_write(ptr, ZR016_MODE, + ZR016_YUV422 | ZR016_YUV422_YUV422 | + (ptr->mode == CODEC_DO_COMPRESSION ? + ZR016_COMPRESSION : ZR016_EXPANSION)); + + // misc setup + zr36016_writei(ptr, ZR016I_SETUP1, + (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | + (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); + zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); + + // Window setup + // (no extra offset for now, norm defines offset, default width height) + zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); + zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); + zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); + zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); + zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); + zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); + zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); + zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); + + /* shall we continue now, please? */ + zr36016_write(ptr, ZR016_GOSTOP, 1); +} + +/* + * CODEC API FUNCTIONS + * + * These functions are accessed by the master via the API structure + */ + +/* + * set compression/expansion mode and launches codec - + * this should be the last call from the master before starting processing + */ +static int zr36016_set_mode(struct videocodec *codec, int mode) +{ + struct zr36016 *ptr = (struct zr36016 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); + + if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) + return -EINVAL; + + ptr->mode = mode; + zr36016_init(ptr); + + return 0; +} + +/* set picture size */ +static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm, + struct vfe_settings *cap, struct vfe_polarity *pol) +{ + struct zr36016 *ptr = (struct zr36016 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", + ptr->name, norm->h_start, norm->v_start, + cap->x, cap->y, cap->width, cap->height, + cap->decimation); + + /* + * if () return -EINVAL; + * trust the master driver that it knows what it does - so + * we allow invalid startx/y for now ... + */ + ptr->width = cap->width; + ptr->height = cap->height; + /* + * (Ronald) This is ugly. zoran_device.c, line 387 + * already mentions what happens if h_start is even + * (blue faces, etc., cr/cb inversed). There's probably + * some good reason why h_start is 0 instead of 1, so I'm + * leaving it to this for now, but really... This can be + * done a lot simpler + */ + ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x; + /* + * Something to note here (I don't understand it), setting + * v_start too high will cause the codec to 'not work'. I + * really don't get it. values of 16 (v_start) already break + * it here. Just '0' seems to work. More testing needed! + */ + ptr->yoff = norm->v_start + cap->y; + /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ + ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; + ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; + + return 0; +} + +/* additional control functions */ +static int zr36016_control(struct videocodec *codec, int type, int size, void *data) +{ + struct zr36016 *ptr = (struct zr36016 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + int *ival = (int *)data; + + zrdev_dbg(zr, "%s: control %d call with %d byte\n", + ptr->name, type, size); + + switch (type) { + case CODEC_G_STATUS: /* get last status - we don't know it ... */ + if (size != sizeof(int)) + return -EFAULT; + *ival = 0; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = 0; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != 0) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + default: + return -EINVAL; + } + + return size; +} + +/* + * Exit and unregister function: + * + * Deinitializes Zoran's JPEG processor + */ + +static int zr36016_unset(struct videocodec *codec) +{ + struct zr36016 *ptr = codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36016_codecs--; + return 0; + } + + return -EFAULT; +} + +/* + * Setup and registry function: + * + * Initializes Zoran's JPEG processor + * + * Also sets pixel size, average code size, mode (compr./decompr.) + * (the given size is determined by the processor with the video interface) + */ + +static int zr36016_setup(struct videocodec *codec) +{ + struct zr36016 *ptr; + struct zoran *zr = videocodec_to_zoran(codec); + int res; + + zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs); + + if (zr36016_codecs == MAX_CODECS) { + zrdev_err(zr, "zr36016: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + codec->data = ptr; + if (!ptr) + return -ENOMEM; + + snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs); + ptr->num = zr36016_codecs++; + ptr->codec = codec; + + //testing + res = zr36016_basic_test(ptr); + if (res < 0) { + zr36016_unset(codec); + return res; + } + //final setup + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 768; + ptr->height = 288; + ptr->xdec = 1; + ptr->ydec = 0; + zr36016_init(ptr); + + zrdev_dbg(zr, "%s: codec v%d attached and running\n", + ptr->name, ptr->version); + + return 0; +} + +static const struct videocodec zr36016_codec = { + .name = "zr36016", + .magic = 0L, /* magic not used */ + .flags = + CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER, + .type = CODEC_TYPE_ZR36016, + .setup = zr36016_setup, /* functionality */ + .unset = zr36016_unset, + .set_mode = zr36016_set_mode, + .set_video = zr36016_set_video, + .control = zr36016_control, + /* others are not used */ +}; + +/* HOOK IN DRIVER AS KERNEL MODULE */ + +int zr36016_init_module(void) +{ + zr36016_codecs = 0; + return videocodec_register(&zr36016_codec); +} + +void zr36016_cleanup_module(void) +{ + if (zr36016_codecs) { + pr_debug("zr36016: something's wrong - %d codecs left somehow.\n", + zr36016_codecs); + } + videocodec_unregister(&zr36016_codec); +} diff --git a/drivers/media/pci/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h new file mode 100644 index 000000000000..04afba35669d --- /dev/null +++ b/drivers/media/pci/zoran/zr36016.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Zoran ZR36016 basic configuration functions - header file + * + * Copyright (C) 2001 Wolfgang Scherr + */ + +#ifndef ZR36016_H +#define ZR36016_H + +/* data stored for each zoran jpeg codec chip */ +struct zr36016 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // coder status + __u8 version; + // actual coder setup + int mode; + + __u16 xoff; + __u16 yoff; + __u16 width; + __u16 height; + __u16 xdec; + __u16 ydec; +}; + +/* direct register addresses */ +#define ZR016_GOSTOP 0x00 +#define ZR016_MODE 0x01 +#define ZR016_IADDR 0x02 +#define ZR016_IDATA 0x03 + +/* indirect register addresses */ +#define ZR016I_SETUP1 0x00 +#define ZR016I_SETUP2 0x01 +#define ZR016I_NAX_LO 0x02 +#define ZR016I_NAX_HI 0x03 +#define ZR016I_PAX_LO 0x04 +#define ZR016I_PAX_HI 0x05 +#define ZR016I_NAY_LO 0x06 +#define ZR016I_NAY_HI 0x07 +#define ZR016I_PAY_LO 0x08 +#define ZR016I_PAY_HI 0x09 +#define ZR016I_NOL_LO 0x0a +#define ZR016I_NOL_HI 0x0b + +/* possible values for mode register */ +#define ZR016_RGB444_YUV444 0x00 +#define ZR016_RGB444_YUV422 0x01 +#define ZR016_RGB444_YUV411 0x02 +#define ZR016_RGB444_Y400 0x03 +#define ZR016_RGB444_RGB444 0x04 +#define ZR016_YUV444_YUV444 0x08 +#define ZR016_YUV444_YUV422 0x09 +#define ZR016_YUV444_YUV411 0x0a +#define ZR016_YUV444_Y400 0x0b +#define ZR016_YUV444_RGB444 0x0c +#define ZR016_YUV422_YUV422 0x11 +#define ZR016_YUV422_YUV411 0x12 +#define ZR016_YUV422_Y400 0x13 +#define ZR016_YUV411_YUV411 0x16 +#define ZR016_YUV411_Y400 0x17 +#define ZR016_4444_4444 0x19 +#define ZR016_100_100 0x1b + +#define ZR016_RGB444 0x00 +#define ZR016_YUV444 0x20 +#define ZR016_YUV422 0x40 + +#define ZR016_COMPRESSION 0x80 +#define ZR016_EXPANSION 0x80 + +/* possible values for setup 1 register */ +#define ZR016_CKRT 0x80 +#define ZR016_VERT 0x40 +#define ZR016_HORZ 0x20 +#define ZR016_HRFL 0x10 +#define ZR016_DSFL 0x08 +#define ZR016_SBFL 0x04 +#define ZR016_RSTR 0x02 +#define ZR016_CNTI 0x01 + +/* possible values for setup 2 register */ +#define ZR016_SYEN 0x40 +#define ZR016_CCIR 0x04 +#define ZR016_SIGN 0x02 +#define ZR016_YMCS 0x01 + +int zr36016_init_module(void); +void zr36016_cleanup_module(void); +#endif /*fndef ZR36016_H */ diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c new file mode 100644 index 000000000000..b07d7e5c1b4a --- /dev/null +++ b/drivers/media/pci/zoran/zr36050.c @@ -0,0 +1,817 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran ZR36050 basic configuration functions + * + * Copyright (C) 2001 Wolfgang Scherr + */ + +#include +#include +#include +#include + +#include +#include + +/* I/O commands, error codes */ +#include + +/* headerfile of this module */ +#include "zr36050.h" + +/* codec io API */ +#include "videocodec.h" + +/* + * it doesn't make sense to have more than 20 or so, + * just to prevent some unwanted loops + */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36050_codecs; + +/* + * Local hardware I/O functions: + * + * read/write via codec layer (registers are located in the master device) + */ + +/* read and write functions */ +static u8 zr36050_read(struct zr36050 *ptr, u16 reg) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + u8 value = 0; + + /* just in case something is wrong... */ + if (ptr->codec->master_data->readreg) + value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; + else + zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); + + zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); + + return value; +} + +static void zr36050_write(struct zr36050 *ptr, u16 reg, u8 value) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); + + /* just in case something is wrong... */ + if (ptr->codec->master_data->writereg) + ptr->codec->master_data->writereg(ptr->codec, reg, value); + else + zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", + ptr->name); +} + +/* status is kept in datastructure */ +static u8 zr36050_read_status1(struct zr36050 *ptr) +{ + ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); + + zr36050_read(ptr, 0); + return ptr->status1; +} + +/* scale factor is kept in datastructure */ +static u16 zr36050_read_scalefactor(struct zr36050 *ptr) +{ + ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | + (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); + + /* leave 0 selected for an eventually GO from master */ + zr36050_read(ptr, 0); + return ptr->scalefact; +} + +/* + * Local helper function: + * + * wait if codec is ready to proceed (end of processing) or time is over + */ + +static void zr36050_wait_end(struct zr36050 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + int i = 0; + + while (!(zr36050_read_status1(ptr) & 0x4)) { + udelay(1); + if (i++ > 200000) { // 200ms, there is for sure something wrong!!! + zrdev_err(zr, + "%s: timeout at wait_end (last status: 0x%02x)\n", + ptr->name, ptr->status1); + break; + } + } +} + +/* + * Local helper function: basic test of "connectivity", writes/reads + * to/from memory the SOF marker + */ + +static int zr36050_basic_test(struct zr36050 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + zr36050_write(ptr, ZR050_SOF_IDX, 0x00); + zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); + if ((zr36050_read(ptr, ZR050_SOF_IDX) | + zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { + zrdev_err(zr, + "%s: attach failed, can't connect to jpeg processor!\n", + ptr->name); + return -ENXIO; + } + zr36050_write(ptr, ZR050_SOF_IDX, 0xff); + zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); + if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | + zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { + zrdev_err(zr, + "%s: attach failed, can't connect to jpeg processor!\n", + ptr->name); + return -ENXIO; + } + + zr36050_wait_end(ptr); + if ((ptr->status1 & 0x4) == 0) { + zrdev_err(zr, + "%s: attach failed, jpeg processor failed (end flag)!\n", + ptr->name); + return -EBUSY; + } + + return 0; /* looks good! */ +} + +/* Local helper function: simple loop for pushing the init datasets */ + +static int zr36050_pushit(struct zr36050 *ptr, u16 startreg, u16 len, const char *data) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + int i = 0; + + zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, + startreg, len); + while (i < len) + zr36050_write(ptr, startreg++, data[i++]); + + return i; +} + +/* + * Basic datasets: + * + * jpeg baseline setup data (you find it on lots places in internet, or just + * extract it from any regular .jpg image...) + * + * Could be variable, but until it's not needed it they are just fixed to save + * memory. Otherwise expand zr36050 structure with arrays, push the values to + * it and initialize from there, as e.g. the linux zr36057/60 driver does it. + */ + +static const char zr36050_dqt[0x86] = { + 0xff, 0xdb, //Marker: DQT + 0x00, 0x84, //Length: 2*65+2 + 0x00, //Pq,Tq first table + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, //Pq,Tq second table + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 +}; + +static const char zr36050_dht[0x1a4] = { + 0xff, 0xc4, //Marker: DHT + 0x01, 0xa2, //Length: 2*AC, 2*DC + 0x00, //DC first table + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x01, //DC second table + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x10, //AC first table + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, + 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, + 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, + 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, + 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, + 0x11, //AC second table + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, + 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, + 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ +#define NO_OF_COMPONENTS 0x3 //Y,U,V +#define BASELINE_PRECISION 0x8 //MCU size (?) +static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT +static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC +static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC + +/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ +static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; +static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; + +/* + * Local helper functions: + * + * calculation and setup of parameter-dependent JPEG baseline segments + * (needed for compression only) + */ + +/* ------------------------------------------------------------------------- */ + +/* + * SOF (start of frame) segment depends on width, height and sampling ratio + * of each color component + */ +static int zr36050_set_sof(struct zr36050 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char sof_data[34]; // max. size of register set + int i; + + zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, + ptr->width, ptr->height, NO_OF_COMPONENTS); + sof_data[0] = 0xff; + sof_data[1] = 0xc0; + sof_data[2] = 0x00; + sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; + sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 + sof_data[5] = (ptr->height) >> 8; + sof_data[6] = (ptr->height) & 0xff; + sof_data[7] = (ptr->width) >> 8; + sof_data[8] = (ptr->width) & 0xff; + sof_data[9] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sof_data[10 + (i * 3)] = i; // index identifier + sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | + (ptr->v_samp_ratio[i]); // sampling ratios + sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection + } + return zr36050_pushit(ptr, ZR050_SOF_IDX, + (3 * NO_OF_COMPONENTS) + 10, sof_data); +} + +/* ------------------------------------------------------------------------- */ + +/* + * SOS (start of scan) segment depends on the used scan components + * of each color component + */ + +static int zr36050_set_sos(struct zr36050 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char sos_data[16]; // max. size of register set + int i; + + zrdev_dbg(zr, "%s: write SOS\n", ptr->name); + sos_data[0] = 0xff; + sos_data[1] = 0xda; + sos_data[2] = 0x00; + sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; + sos_data[4] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sos_data[5 + (i * 2)] = i; // index + sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. + } + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; + return zr36050_pushit(ptr, ZR050_SOS1_IDX, + 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, + sos_data); +} + +/* ------------------------------------------------------------------------- */ + +/* DRI (define restart interval) */ + +static int zr36050_set_dri(struct zr36050 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char dri_data[6]; // max. size of register set + + zrdev_dbg(zr, "%s: write DRI\n", ptr->name); + dri_data[0] = 0xff; + dri_data[1] = 0xdd; + dri_data[2] = 0x00; + dri_data[3] = 0x04; + dri_data[4] = ptr->dri >> 8; + dri_data[5] = ptr->dri & 0xff; + return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); +} + +/* + * Setup function: + * + * Setup compression/decompression of Zoran's JPEG processor + * ( see also zoran 36050 manual ) + * + * ... sorry for the spaghetti code ... + */ +static void zr36050_init(struct zr36050 *ptr) +{ + int sum = 0; + long bitcnt, tmp; + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + if (ptr->mode == CODEC_DO_COMPRESSION) { + zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); + + /* 050 communicates with 057 in master mode */ + zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); + + /* encoding table preload for compression */ + zr36050_write(ptr, ZR050_MODE, + ZR050_MO_COMP | ZR050_MO_TLM); + zr36050_write(ptr, ZR050_OPTIONS, 0); + + /* disable all IRQs */ + zr36050_write(ptr, ZR050_INT_REQ_0, 0); + zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 + + /* volume control settings */ + /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ + zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); + zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); + + zr36050_write(ptr, ZR050_AF_HI, 0xff); + zr36050_write(ptr, ZR050_AF_M, 0xff); + zr36050_write(ptr, ZR050_AF_LO, 0xff); + + /* setup the variable jpeg tables */ + sum += zr36050_set_sof(ptr); + sum += zr36050_set_sos(ptr); + sum += zr36050_set_dri(ptr); + + /* + * setup the fixed jpeg tables - maybe variable, though - + * (see table init section above) + */ + zrdev_dbg(zr, "%s: write DQT, DHT, APP\n", ptr->name); + sum += zr36050_pushit(ptr, ZR050_DQT_IDX, + sizeof(zr36050_dqt), zr36050_dqt); + sum += zr36050_pushit(ptr, ZR050_DHT_IDX, + sizeof(zr36050_dht), zr36050_dht); + zr36050_write(ptr, ZR050_APP_IDX, 0xff); + zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); + zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); + zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); + sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, + ptr->app.data) + 4; + zr36050_write(ptr, ZR050_COM_IDX, 0xff); + zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); + zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); + zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); + sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, + ptr->com.data) + 4; + + /* do the internal huffman table preload */ + zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); + + zr36050_write(ptr, ZR050_GO, 1); // launch codec + zr36050_wait_end(ptr); + zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", + ptr->name, ptr->status1); + + if ((ptr->status1 & 0x4) == 0) { + zrdev_err(zr, "%s: init aborted!\n", ptr->name); + return; // something is wrong, its timed out!!!! + } + + /* setup misc. data for compression (target code sizes) */ + + /* size of compressed code to reach without header data */ + sum = ptr->real_code_vol - sum; + bitcnt = sum << 3; /* need the size in bits */ + + tmp = bitcnt >> 16; + zrdev_dbg(zr, + "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", + ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); + zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); + + bitcnt -= bitcnt >> 7; // bits without stuffing + bitcnt -= ((bitcnt * 5) >> 6); // bits without eob + + tmp = bitcnt >> 16; + zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", + ptr->name, bitcnt, tmp); + zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); + + /* compression setup with or without bitrate control */ + zr36050_write(ptr, ZR050_MODE, + ZR050_MO_COMP | ZR050_MO_PASS2 | + (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); + + /* this headers seem to deliver "valid AVI" jpeg frames */ + zr36050_write(ptr, ZR050_MARKERS_EN, + ZR050_ME_DQT | ZR050_ME_DHT | + ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | + ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); + } else { + zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); + + /* 050 communicates with 055 in master mode */ + zr36050_write(ptr, ZR050_HARDWARE, + ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); + + /* encoding table preload */ + zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); + + /* disable all IRQs */ + zr36050_write(ptr, ZR050_INT_REQ_0, 0); + zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 + + zrdev_dbg(zr, "%s: write DHT\n", ptr->name); + zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), + zr36050_dht); + + /* do the internal huffman table preload */ + zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); + + zr36050_write(ptr, ZR050_GO, 1); // launch codec + zr36050_wait_end(ptr); + zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", + ptr->name, ptr->status1); + + if ((ptr->status1 & 0x4) == 0) { + zrdev_err(zr, "%s: init aborted!\n", ptr->name); + return; // something is wrong, its timed out!!!! + } + + /* setup misc. data for expansion */ + zr36050_write(ptr, ZR050_MODE, 0); + zr36050_write(ptr, ZR050_MARKERS_EN, 0); + } + + /* adr on selected, to allow GO from master */ + zr36050_read(ptr, 0); +} + +/* + * CODEC API FUNCTIONS + * + * this functions are accessed by the master via the API structure + */ + +/* + * set compression/expansion mode and launches codec - + * this should be the last call from the master before starting processing + */ +static int zr36050_set_mode(struct videocodec *codec, int mode) +{ + struct zr36050 *ptr = (struct zr36050 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); + + if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) + return -EINVAL; + + ptr->mode = mode; + zr36050_init(ptr); + + return 0; +} + +/* set picture size (norm is ignored as the codec doesn't know about it) */ +static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm, + struct vfe_settings *cap, struct vfe_polarity *pol) +{ + struct zr36050 *ptr = (struct zr36050 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + int size; + + zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", + ptr->name, norm->h_start, norm->v_start, + cap->x, cap->y, cap->width, cap->height, + cap->decimation, cap->quality); + /* + * trust the master driver that it knows what it does - so + * we allow invalid startx/y and norm for now ... + */ + ptr->width = cap->width / (cap->decimation & 0xff); + ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); + + /* (KM) JPEG quality */ + size = ptr->width * ptr->height; + size *= 16; /* size in bits */ + /* apply quality setting */ + size = size * cap->quality / 200; + + /* Minimum: 1kb */ + if (size < 8192) + size = 8192; + /* Maximum: 7/8 of code buffer */ + if (size > ptr->total_code_vol * 7) + size = ptr->total_code_vol * 7; + + ptr->real_code_vol = size >> 3; /* in bytes */ + + /* + * Set max_block_vol here (previously in zr36050_init, moved + * here for consistency with zr36060 code + */ + zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); + + return 0; +} + +/* additional control functions */ +static int zr36050_control(struct videocodec *codec, int type, int size, void *data) +{ + struct zr36050 *ptr = (struct zr36050 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + int *ival = (int *)data; + + zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, + size); + + switch (type) { + case CODEC_G_STATUS: /* get last status */ + if (size != sizeof(int)) + return -EFAULT; + zr36050_read_status1(ptr); + *ival = ptr->status1; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = CODEC_MODE_BJPG; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != CODEC_MODE_BJPG) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + /* not needed, do nothing */ + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + *ival = ptr->total_code_vol; + break; + + case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + ptr->total_code_vol = *ival; + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + break; + + case CODEC_G_JPEG_SCALE: /* get scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + *ival = zr36050_read_scalefactor(ptr); + break; + + case CODEC_S_JPEG_SCALE: /* set scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + ptr->scalefact = *ival; + break; + + case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + *app = ptr->app; + break; + } + + case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + ptr->app = *app; + break; + } + + case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + *com = ptr->com; + break; + } + + case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + ptr->com = *com; + break; + } + + default: + return -EINVAL; + } + + return size; +} + +/* Exit and unregister function: Deinitializes Zoran's JPEG processor */ + +static int zr36050_unset(struct videocodec *codec) +{ + struct zr36050 *ptr = codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, + ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36050_codecs--; + return 0; + } + + return -EFAULT; +} + +/* + * Setup and registry function: + * + * Initializes Zoran's JPEG processor + * + * Also sets pixel size, average code size, mode (compr./decompr.) + * (the given size is determined by the processor with the video interface) + */ + +static int zr36050_setup(struct videocodec *codec) +{ + struct zr36050 *ptr; + struct zoran *zr = videocodec_to_zoran(codec); + int res; + + zrdev_dbg(zr, "zr36050: initializing MJPEG subsystem #%d.\n", + zr36050_codecs); + + if (zr36050_codecs == MAX_CODECS) { + zrdev_err(zr, + "zr36050: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + codec->data = ptr; + if (!ptr) + return -ENOMEM; + + snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", + zr36050_codecs); + ptr->num = zr36050_codecs++; + ptr->codec = codec; + + //testing + res = zr36050_basic_test(ptr); + if (res < 0) { + zr36050_unset(codec); + return res; + } + //final setup + memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); + memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); + + /* 0 or 1 - fixed file size flag (what is the difference?) */ + ptr->bitrate_ctrl = 0; + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 384; + ptr->height = 288; + ptr->total_code_vol = 16000; + ptr->max_block_vol = 240; + ptr->scalefact = 0x100; + ptr->dri = 1; + + /* no app/com marker by default */ + ptr->app.appn = 0; + ptr->app.len = 0; + ptr->com.len = 0; + + zr36050_init(ptr); + + zrdev_info(zr, "%s: codec attached and running\n", + ptr->name); + + return 0; +} + +static const struct videocodec zr36050_codec = { + .name = "zr36050", + .magic = 0L, // magic not used + .flags = + CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER, + .type = CODEC_TYPE_ZR36050, + .setup = zr36050_setup, // functionality + .unset = zr36050_unset, + .set_mode = zr36050_set_mode, + .set_video = zr36050_set_video, + .control = zr36050_control, + // others are not used +}; + +/* HOOK IN DRIVER AS KERNEL MODULE */ + +int zr36050_init_module(void) +{ + zr36050_codecs = 0; + return videocodec_register(&zr36050_codec); +} + +void zr36050_cleanup_module(void) +{ + if (zr36050_codecs) { + pr_debug("zr36050: something's wrong - %d codecs left somehow.\n", + zr36050_codecs); + } + videocodec_unregister(&zr36050_codec); +} diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h new file mode 100644 index 000000000000..f9b58f4c77b9 --- /dev/null +++ b/drivers/media/pci/zoran/zr36050.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Zoran ZR36050 basic configuration functions - header file + * + * Copyright (C) 2001 Wolfgang Scherr + */ + +#ifndef ZR36050_H +#define ZR36050_H + +#include "videocodec.h" + +/* data stored for each zoran jpeg codec chip */ +struct zr36050 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // last coder status + __u8 status1; + // actual coder setup + int mode; + + __u16 width; + __u16 height; + + __u16 bitrate_ctrl; + + __u32 total_code_vol; + __u32 real_code_vol; + __u16 max_block_vol; + + __u8 h_samp_ratio[8]; + __u8 v_samp_ratio[8]; + __u16 scalefact; + __u16 dri; + + /* com/app marker */ + struct jpeg_com_marker com; + struct jpeg_app_marker app; +}; + +/* zr36050 register addresses */ +#define ZR050_GO 0x000 +#define ZR050_HARDWARE 0x002 +#define ZR050_MODE 0x003 +#define ZR050_OPTIONS 0x004 +#define ZR050_MBCV 0x005 +#define ZR050_MARKERS_EN 0x006 +#define ZR050_INT_REQ_0 0x007 +#define ZR050_INT_REQ_1 0x008 +#define ZR050_TCV_NET_HI 0x009 +#define ZR050_TCV_NET_MH 0x00a +#define ZR050_TCV_NET_ML 0x00b +#define ZR050_TCV_NET_LO 0x00c +#define ZR050_TCV_DATA_HI 0x00d +#define ZR050_TCV_DATA_MH 0x00e +#define ZR050_TCV_DATA_ML 0x00f +#define ZR050_TCV_DATA_LO 0x010 +#define ZR050_SF_HI 0x011 +#define ZR050_SF_LO 0x012 +#define ZR050_AF_HI 0x013 +#define ZR050_AF_M 0x014 +#define ZR050_AF_LO 0x015 +#define ZR050_ACV_HI 0x016 +#define ZR050_ACV_MH 0x017 +#define ZR050_ACV_ML 0x018 +#define ZR050_ACV_LO 0x019 +#define ZR050_ACT_HI 0x01a +#define ZR050_ACT_MH 0x01b +#define ZR050_ACT_ML 0x01c +#define ZR050_ACT_LO 0x01d +#define ZR050_ACV_TURN_HI 0x01e +#define ZR050_ACV_TURN_MH 0x01f +#define ZR050_ACV_TURN_ML 0x020 +#define ZR050_ACV_TURN_LO 0x021 +#define ZR050_STATUS_0 0x02e +#define ZR050_STATUS_1 0x02f + +#define ZR050_SOF_IDX 0x040 +#define ZR050_SOS1_IDX 0x07a +#define ZR050_SOS2_IDX 0x08a +#define ZR050_SOS3_IDX 0x09a +#define ZR050_SOS4_IDX 0x0aa +#define ZR050_DRI_IDX 0x0c0 +#define ZR050_DNL_IDX 0x0c6 +#define ZR050_DQT_IDX 0x0cc +#define ZR050_DHT_IDX 0x1d4 +#define ZR050_APP_IDX 0x380 +#define ZR050_COM_IDX 0x3c0 + +/* zr36050 hardware register bits */ + +#define ZR050_HW_BSWD 0x80 +#define ZR050_HW_MSTR 0x40 +#define ZR050_HW_DMA 0x20 +#define ZR050_HW_CFIS_1_CLK 0x00 +#define ZR050_HW_CFIS_2_CLK 0x04 +#define ZR050_HW_CFIS_3_CLK 0x08 +#define ZR050_HW_CFIS_4_CLK 0x0C +#define ZR050_HW_CFIS_5_CLK 0x10 +#define ZR050_HW_CFIS_6_CLK 0x14 +#define ZR050_HW_CFIS_7_CLK 0x18 +#define ZR050_HW_CFIS_8_CLK 0x1C +#define ZR050_HW_BELE 0x01 + +/* zr36050 mode register bits */ + +#define ZR050_MO_COMP 0x80 +#define ZR050_MO_ATP 0x40 +#define ZR050_MO_PASS2 0x20 +#define ZR050_MO_TLM 0x10 +#define ZR050_MO_DCONLY 0x08 +#define ZR050_MO_BRC 0x04 + +#define ZR050_MO_ATP 0x40 +#define ZR050_MO_PASS2 0x20 +#define ZR050_MO_TLM 0x10 +#define ZR050_MO_DCONLY 0x08 + +/* zr36050 option register bits */ + +#define ZR050_OP_NSCN_1 0x00 +#define ZR050_OP_NSCN_2 0x20 +#define ZR050_OP_NSCN_3 0x40 +#define ZR050_OP_NSCN_4 0x60 +#define ZR050_OP_NSCN_5 0x80 +#define ZR050_OP_NSCN_6 0xA0 +#define ZR050_OP_NSCN_7 0xC0 +#define ZR050_OP_NSCN_8 0xE0 +#define ZR050_OP_OVF 0x10 + +/* zr36050 markers-enable register bits */ + +#define ZR050_ME_APP 0x80 +#define ZR050_ME_COM 0x40 +#define ZR050_ME_DRI 0x20 +#define ZR050_ME_DQT 0x10 +#define ZR050_ME_DHT 0x08 +#define ZR050_ME_DNL 0x04 +#define ZR050_ME_DQTI 0x02 +#define ZR050_ME_DHTI 0x01 + +/* zr36050 status0/1 register bit masks */ + +#define ZR050_ST_RST_MASK 0x20 +#define ZR050_ST_SOF_MASK 0x02 +#define ZR050_ST_SOS_MASK 0x02 +#define ZR050_ST_DATRDY_MASK 0x80 +#define ZR050_ST_MRKDET_MASK 0x40 +#define ZR050_ST_RFM_MASK 0x10 +#define ZR050_ST_RFD_MASK 0x08 +#define ZR050_ST_END_MASK 0x04 +#define ZR050_ST_TCVOVF_MASK 0x02 +#define ZR050_ST_DATOVF_MASK 0x01 + +/* pixel component idx */ + +#define ZR050_Y_COMPONENT 0 +#define ZR050_U_COMPONENT 1 +#define ZR050_V_COMPONENT 2 + +int zr36050_init_module(void); +void zr36050_cleanup_module(void); +#endif /*fndef ZR36050_H */ diff --git a/drivers/media/pci/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h new file mode 100644 index 000000000000..45d8afc62b37 --- /dev/null +++ b/drivers/media/pci/zoran/zr36057.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * zr36057.h - zr36057 register offsets + * + * Copyright (C) 1998 Dave Perks + */ + +#ifndef _ZR36057_H_ +#define _ZR36057_H_ + +/* Zoran ZR36057 registers */ + +#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ +#define ZR36057_VFEHCR_HS_POL BIT(30) +#define ZR36057_VFEHCR_H_START 10 +#define ZR36057_VFEHCR_H_END 0 +#define ZR36057_VFEHCR_HMASK 0x3ff + +#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ +#define ZR36057_VFEVCR_VS_POL BIT(30) +#define ZR36057_VFEVCR_V_START 10 +#define ZR36057_VFEVCR_V_END 0 +#define ZR36057_VFEVCR_VMASK 0x3ff + +#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ +#define ZR36057_VFESPFR_EXT_FL BIT(26) +#define ZR36057_VFESPFR_TOP_FIELD BIT(25) +#define ZR36057_VFESPFR_VCLK_POL BIT(24) +#define ZR36057_VFESPFR_H_FILTER 21 +#define ZR36057_VFESPFR_HOR_DCM 14 +#define ZR36057_VFESPFR_VER_DCM 8 +#define ZR36057_VFESPFR_DISP_MODE 6 +#define ZR36057_VFESPFR_YUV422 (0 << 3) +#define ZR36057_VFESPFR_RGB888 (1 << 3) +#define ZR36057_VFESPFR_RGB565 (2 << 3) +#define ZR36057_VFESPFR_RGB555 (3 << 3) +#define ZR36057_VFESPFR_ERR_DIF BIT(2) +#define ZR36057_VFESPFR_PACK24 BIT(1) +#define ZR36057_VFESPFR_LITTLE_ENDIAN BIT(0) + +#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ + +#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ + +#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ +#define ZR36057_VSSFGR_DISP_STRIDE 16 +#define ZR36057_VSSFGR_VID_OVF BIT(8) +#define ZR36057_VSSFGR_SNAP_SHOT BIT(1) +#define ZR36057_VSSFGR_FRAME_GRAB BIT(0) + +#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ +#define ZR36057_VDCR_VID_EN BIT(31) +#define ZR36057_VDCR_MIN_PIX 24 +#define ZR36057_VDCR_TRITON BIT(24) +#define ZR36057_VDCR_VID_WIN_HT 12 +#define ZR36057_VDCR_VID_WIN_WID 0 + +#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ + +#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ + +#define ZR36057_OCR 0x024 /* Overlay Control Register */ +#define ZR36057_OCR_OVL_ENABLE BIT(15) +#define ZR36057_OCR_MASK_STRIDE 0 + +#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ +#define ZR36057_SPGPPCR_SOFT_RESET BIT(24) + +#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ + +#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ + +#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ +#define ZR36057_MCTCR_COD_TIME BIT(30) +#define ZR36057_MCTCR_C_EMPTY BIT(29) +#define ZR36057_MCTCR_C_FLUSH BIT(28) +#define ZR36057_MCTCR_COD_GUEST_ID 20 +#define ZR36057_MCTCR_COD_GUEST_REG 16 + +#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ + +#define ZR36057_ISR 0x03c /* Interrupt Status Register */ +#define ZR36057_ISR_GIRQ1 BIT(30) +#define ZR36057_ISR_GIRQ0 BIT(29) +#define ZR36057_ISR_COD_REP_IRQ BIT(28) +#define ZR36057_ISR_JPEG_REP_IRQ BIT(27) + +#define ZR36057_ICR 0x040 /* Interrupt Control Register */ +#define ZR36057_ICR_GIRQ1 BIT(30) +#define ZR36057_ICR_GIRQ0 BIT(29) +#define ZR36057_ICR_COD_REP_IRQ BIT(28) +#define ZR36057_ICR_JPEG_REP_IRQ BIT(27) +#define ZR36057_ICR_INT_PIN_EN BIT(24) + +#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ +#define ZR36057_I2CBR_SDA BIT(1) +#define ZR36057_I2CBR_SCL BIT(0) + +#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ +#define ZR36057_JMC_JPG BIT(31) +#define ZR36057_JMC_JPG_EXP_MODE (0 << 29) +#define ZR36057_JMC_JPG_CMP_MODE BIT(29) +#define ZR36057_JMC_MJPG_EXP_MODE (2 << 29) +#define ZR36057_JMC_MJPG_CMP_MODE (3 << 29) +#define ZR36057_JMC_RTBUSY_FB BIT(6) +#define ZR36057_JMC_GO_EN BIT(5) +#define ZR36057_JMC_SYNC_MSTR BIT(4) +#define ZR36057_JMC_FLD_PER_BUFF BIT(3) +#define ZR36057_JMC_VFIFO_FB BIT(2) +#define ZR36057_JMC_CFIFO_FB BIT(1) +#define ZR36057_JMC_STLL_LIT_ENDIAN BIT(0) + +#define ZR36057_JPC 0x104 /* JPEG Process Control */ +#define ZR36057_JPC_P_RESET BIT(7) +#define ZR36057_JPC_COD_TRNS_EN BIT(5) +#define ZR36057_JPC_ACTIVE BIT(0) + +#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ +#define ZR36057_VSP_VSYNC_SIZE 16 +#define ZR36057_VSP_FRM_TOT 0 + +#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ +#define ZR36057_HSP_HSYNC_START 16 +#define ZR36057_HSP_LINE_TOT 0 + +#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ +#define ZR36057_FHAP_NAX 16 +#define ZR36057_FHAP_PAX 0 + +#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ +#define ZR36057_FVAP_NAY 16 +#define ZR36057_FVAP_PAY 0 + +#define ZR36057_FPP 0x118 /* Field Process Parameters */ +#define ZR36057_FPP_ODD_EVEN BIT(0) + +#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ + +#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ + +#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ +#define ZR36057_JCGI_JPE_GUEST_ID 4 +#define ZR36057_JCGI_JPE_GUEST_REG 0 + +#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ + +#define ZR36057_POR 0x200 /* Post Office Register */ +#define ZR36057_POR_PO_PEN BIT(25) +#define ZR36057_POR_PO_TIME BIT(24) +#define ZR36057_POR_PO_DIR BIT(23) + +#define ZR36057_STR 0x300 /* "Still" Transfer Register */ + +#endif diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c new file mode 100644 index 000000000000..75fd167603dc --- /dev/null +++ b/drivers/media/pci/zoran/zr36060.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran ZR36060 basic configuration functions + * + * Copyright (C) 2002 Laurent Pinchart + */ + +#include +#include +#include +#include + +#include +#include + +/* I/O commands, error codes */ +#include + +/* headerfile of this module */ +#include "zr36060.h" + +/* codec io API */ +#include "videocodec.h" + +/* it doesn't make sense to have more than 20 or so, just to prevent some unwanted loops */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36060_codecs; + +static bool low_bitrate; +module_param(low_bitrate, bool, 0); +MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); + +/* ========================================================================= + * Local hardware I/O functions: + * read/write via codec layer (registers are located in the master device) + * ========================================================================= + */ + +static u8 zr36060_read(struct zr36060 *ptr, u16 reg) +{ + u8 value = 0; + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + // just in case something is wrong... + if (ptr->codec->master_data->readreg) + value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xff; + else + zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); + + return value; +} + +static void zr36060_write(struct zr36060 *ptr, u16 reg, u8 value) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + zrdev_dbg(zr, "0x%02x @0x%04x\n", value, reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) + ptr->codec->master_data->writereg(ptr->codec, reg, value); + else + zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); +} + +/* ========================================================================= + * Local helper function: + * status read + * ========================================================================= + */ + +/* status is kept in datastructure */ +static u8 zr36060_read_status(struct zr36060 *ptr) +{ + ptr->status = zr36060_read(ptr, ZR060_CFSR); + + zr36060_read(ptr, 0); + return ptr->status; +} + +/* scale factor is kept in datastructure */ +static u16 zr36060_read_scalefactor(struct zr36060 *ptr) +{ + ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | + (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); + + /* leave 0 selected for an eventually GO from master */ + zr36060_read(ptr, 0); + return ptr->scalefact; +} + +/* wait if codec is ready to proceed (end of processing) or time is over */ +static void zr36060_wait_end(struct zr36060 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + int i = 0; + + while (zr36060_read_status(ptr) & ZR060_CFSR_BUSY) { + udelay(1); + if (i++ > 200000) { // 200ms, there is for sure something wrong!!! + zrdev_dbg(zr, + "%s: timeout at wait_end (last status: 0x%02x)\n", + ptr->name, ptr->status); + break; + } + } +} + +/* Basic test of "connectivity", writes/reads to/from memory the SOF marker */ +static int zr36060_basic_test(struct zr36060 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && + (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { + zrdev_err(zr, "%s: attach failed, can't connect to jpeg processor!\n", ptr->name); + return -ENXIO; + } + + zr36060_wait_end(ptr); + if (ptr->status & ZR060_CFSR_BUSY) { + zrdev_err(zr, "%s: attach failed, jpeg processor failed (end flag)!\n", ptr->name); + return -EBUSY; + } + + return 0; /* looks good! */ +} + +/* simple loop for pushing the init datasets */ +static int zr36060_pushit(struct zr36060 *ptr, u16 startreg, u16 len, const char *data) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + int i = 0; + + zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, + startreg, len); + while (i < len) + zr36060_write(ptr, startreg++, data[i++]); + + return i; +} + +/* ========================================================================= + * Basic datasets: + * jpeg baseline setup data (you find it on lots places in internet, or just + * extract it from any regular .jpg image...) + * + * Could be variable, but until it's not needed it they are just fixed to save + * memory. Otherwise expand zr36060 structure with arrays, push the values to + * it and initialize from there, as e.g. the linux zr36057/60 driver does it. + * ========================================================================= + */ +static const char zr36060_dqt[0x86] = { + 0xff, 0xdb, //Marker: DQT + 0x00, 0x84, //Length: 2*65+2 + 0x00, //Pq,Tq first table + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, //Pq,Tq second table + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 +}; + +static const char zr36060_dht[0x1a4] = { + 0xff, 0xc4, //Marker: DHT + 0x01, 0xa2, //Length: 2*AC, 2*DC + 0x00, //DC first table + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x01, //DC second table + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x10, //AC first table + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, + 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, + 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, + 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, + 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, + 0x11, //AC second table + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, + 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, + 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ +#define NO_OF_COMPONENTS 0x3 //Y,U,V +#define BASELINE_PRECISION 0x8 //MCU size (?) +static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT +static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC +static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC + +/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ +static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; +static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; + +/* + * SOF (start of frame) segment depends on width, height and sampling ratio + * of each color component + */ +static int zr36060_set_sof(struct zr36060 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char sof_data[34]; // max. size of register set + int i; + + zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, + ptr->width, ptr->height, NO_OF_COMPONENTS); + sof_data[0] = 0xff; + sof_data[1] = 0xc0; + sof_data[2] = 0x00; + sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; + sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 + sof_data[5] = (ptr->height) >> 8; + sof_data[6] = (ptr->height) & 0xff; + sof_data[7] = (ptr->width) >> 8; + sof_data[8] = (ptr->width) & 0xff; + sof_data[9] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sof_data[10 + (i * 3)] = i; // index identifier + sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | + (ptr->v_samp_ratio[i]); // sampling ratios + sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection + } + return zr36060_pushit(ptr, ZR060_SOF_IDX, + (3 * NO_OF_COMPONENTS) + 10, sof_data); +} + +/* SOS (start of scan) segment depends on the used scan components of each color component */ +static int zr36060_set_sos(struct zr36060 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char sos_data[16]; // max. size of register set + int i; + + zrdev_dbg(zr, "%s: write SOS\n", ptr->name); + sos_data[0] = 0xff; + sos_data[1] = 0xda; + sos_data[2] = 0x00; + sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; + sos_data[4] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sos_data[5 + (i * 2)] = i; // index + sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | + zr36060_ta[i]; // AC/DC tbl.sel. + } + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; + return zr36060_pushit(ptr, ZR060_SOS_IDX, + 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, + sos_data); +} + +/* DRI (define restart interval) */ +static int zr36060_set_dri(struct zr36060 *ptr) +{ + struct zoran *zr = videocodec_to_zoran(ptr->codec); + char dri_data[6]; // max. size of register set + + zrdev_dbg(zr, "%s: write DRI\n", ptr->name); + dri_data[0] = 0xff; + dri_data[1] = 0xdd; + dri_data[2] = 0x00; + dri_data[3] = 0x04; + dri_data[4] = (ptr->dri) >> 8; + dri_data[5] = (ptr->dri) & 0xff; + return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); +} + +/* Setup compression/decompression of Zoran's JPEG processor ( see also zoran 36060 manual ) + * ... sorry for the spaghetti code ... + */ +static void zr36060_init(struct zr36060 *ptr) +{ + int sum = 0; + long bitcnt, tmp; + struct zoran *zr = videocodec_to_zoran(ptr->codec); + + if (ptr->mode == CODEC_DO_COMPRESSION) { + zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); + + /* 060 communicates with 067 in master mode */ + zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); + + /* Compression with or without variable scale factor */ + /*FIXME: What about ptr->bitrate_ctrl? */ + zr36060_write(ptr, ZR060_CMR, ZR060_CMR_COMP | ZR060_CMR_PASS2 | ZR060_CMR_BRB); + + /* Must be zero */ + zr36060_write(ptr, ZR060_MBZ, 0x00); + zr36060_write(ptr, ZR060_TCR_HI, 0x00); + zr36060_write(ptr, ZR060_TCR_LO, 0x00); + + /* Disable all IRQs - no DataErr means autoreset */ + zr36060_write(ptr, ZR060_IMR, 0); + + /* volume control settings */ + zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); + zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); + + zr36060_write(ptr, ZR060_AF_HI, 0xff); + zr36060_write(ptr, ZR060_AF_M, 0xff); + zr36060_write(ptr, ZR060_AF_LO, 0xff); + + /* setup the variable jpeg tables */ + sum += zr36060_set_sof(ptr); + sum += zr36060_set_sos(ptr); + sum += zr36060_set_dri(ptr); + +/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ + sum += zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), zr36060_dqt); + sum += zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); + zr36060_write(ptr, ZR060_APP_IDX, 0xff); + zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); + zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); + zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); + sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, ptr->app.data) + 4; + zr36060_write(ptr, ZR060_COM_IDX, 0xff); + zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); + zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); + zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); + sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, ptr->com.data) + 4; + + /* setup misc. data for compression (target code sizes) */ + + /* size of compressed code to reach without header data */ + sum = ptr->real_code_vol - sum; + bitcnt = sum << 3; /* need the size in bits */ + + tmp = bitcnt >> 16; + zrdev_dbg(zr, + "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", + ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); + zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); + + bitcnt -= bitcnt >> 7; // bits without stuffing + bitcnt -= ((bitcnt * 5) >> 6); // bits without eob + + tmp = bitcnt >> 16; + zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", + ptr->name, bitcnt, tmp); + zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); + + /* JPEG markers to be included in the compressed stream */ + zr36060_write(ptr, ZR060_MER, + ZR060_MER_DQT | ZR060_MER_DHT | + ((ptr->com.len > 0) ? ZR060_MER_COM : 0) | + ((ptr->app.len > 0) ? ZR060_MER_APP : 0)); + + /* Setup the Video Frontend */ + /* Limit pixel range to 16..235 as per CCIR-601 */ + zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); + + } else { + zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); + + /* 060 communicates with 067 in master mode */ + zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); + + /* Decompression */ + zr36060_write(ptr, ZR060_CMR, 0); + + /* Must be zero */ + zr36060_write(ptr, ZR060_MBZ, 0x00); + zr36060_write(ptr, ZR060_TCR_HI, 0x00); + zr36060_write(ptr, ZR060_TCR_LO, 0x00); + + /* Disable all IRQs - no DataErr means autoreset */ + zr36060_write(ptr, ZR060_IMR, 0); + + /* setup misc. data for expansion */ + zr36060_write(ptr, ZR060_MER, 0); + +/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ + zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); + + /* Setup the Video Frontend */ + //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FI_EXT); + //this doesn't seem right and doesn't work... + zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); + } + + /* Load the tables */ + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST | ZR060_LOAD_LOAD); + zr36060_wait_end(ptr); + zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", + ptr->name, ptr->status); + + if (ptr->status & ZR060_CFSR_BUSY) { + zrdev_err(zr, "%s: init aborted!\n", ptr->name); + return; // something is wrong, its timed out!!!! + } +} + +/* ========================================================================= + * CODEC API FUNCTIONS + * this functions are accessed by the master via the API structure + * ========================================================================= + */ + +/* set compressiion/expansion mode and launches codec - + * this should be the last call from the master before starting processing + */ +static int zr36060_set_mode(struct videocodec *codec, int mode) +{ + struct zr36060 *ptr = (struct zr36060 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); + + if (mode != CODEC_DO_EXPANSION && mode != CODEC_DO_COMPRESSION) + return -EINVAL; + + ptr->mode = mode; + zr36060_init(ptr); + + return 0; +} + +/* set picture size (norm is ignored as the codec doesn't know about it) */ +static int zr36060_set_video(struct videocodec *codec, const struct tvnorm *norm, + struct vfe_settings *cap, struct vfe_polarity *pol) +{ + struct zr36060 *ptr = (struct zr36060 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + u32 reg; + int size; + + zrdev_dbg(zr, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, + cap->x, cap->y, cap->width, cap->height, cap->decimation); + + /* if () return -EINVAL; + * trust the master driver that it knows what it does - so + * we allow invalid startx/y and norm for now ... + */ + ptr->width = cap->width / (cap->decimation & 0xff); + ptr->height = cap->height / (cap->decimation >> 8); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); + + /* Note that VSPol/HSPol bits in zr36060 have the opposite + * meaning of their zr360x7 counterparts with the same names + * N.b. for VSPol this is only true if FIVEdge = 0 (default, + * left unchanged here - in accordance with datasheet). + */ + reg = (!pol->vsync_pol ? ZR060_VPR_VS_POL : 0) + | (!pol->hsync_pol ? ZR060_VPR_HS_POL : 0) + | (pol->field_pol ? ZR060_VPR_FI_POL : 0) + | (pol->blank_pol ? ZR060_VPR_BL_POL : 0) + | (pol->subimg_pol ? ZR060_VPR_S_IMG_POL : 0) + | (pol->poe_pol ? ZR060_VPR_POE_POL : 0) + | (pol->pvalid_pol ? ZR060_VPR_P_VAL_POL : 0) + | (pol->vclk_pol ? ZR060_VPR_VCLK_POL : 0); + zr36060_write(ptr, ZR060_VPR, reg); + + reg = 0; + switch (cap->decimation & 0xff) { + default: + case 1: + break; + + case 2: + reg |= ZR060_SR_H_SCALE2; + break; + + case 4: + reg |= ZR060_SR_H_SCALE4; + break; + } + + switch (cap->decimation >> 8) { + default: + case 1: + break; + + case 2: + reg |= ZR060_SR_V_SCALE; + break; + } + zr36060_write(ptr, ZR060_SR, reg); + + zr36060_write(ptr, ZR060_BCR_Y, 0x00); + zr36060_write(ptr, ZR060_BCR_U, 0x80); + zr36060_write(ptr, ZR060_BCR_V, 0x80); + + /* sync generator */ + + reg = norm->ht - 1; /* Vtotal */ + zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); + + reg = norm->wt - 1; /* Htotal */ + zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); + + reg = 6 - 1; /* VsyncSize */ + zr36060_write(ptr, ZR060_SGR_VSYNC, reg); + + reg = 68; + zr36060_write(ptr, ZR060_SGR_HSYNC, reg); + + reg = norm->v_start - 1; /* BVstart */ + zr36060_write(ptr, ZR060_SGR_BVSTART, reg); + + reg += norm->ha / 2; /* BVend */ + zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); + + reg = norm->h_start - 1; /* BHstart */ + zr36060_write(ptr, ZR060_SGR_BHSTART, reg); + + reg += norm->wa; /* BHend */ + zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); + + /* active area */ + reg = cap->y + norm->v_start; /* Vstart */ + zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); + + reg += cap->height; /* Vend */ + zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); + + reg = cap->x + norm->h_start; /* Hstart */ + zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); + + reg += cap->width; /* Hend */ + zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); + + /* subimage area */ + reg = norm->v_start - 4; /* SVstart */ + zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); + + reg += norm->ha / 2 + 8; /* SVend */ + zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); + + reg = norm->h_start /*+ 64 */ - 4; /* SHstart */ + zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); + + reg += norm->wa + 8; /* SHend */ + zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); + + size = ptr->width * ptr->height; + /* Target compressed field size in bits: */ + size = size * 16; /* uncompressed size in bits */ + /* (Ronald) by default, quality = 100 is a compression + * ratio 1:2. Setting low_bitrate (insmod option) sets + * it to 1:4 (instead of 1:2, zr36060 max) as limit because the + * buz can't handle more at decimation=1... Use low_bitrate if + * you have a Buz, unless you know what you're doing + */ + size = size * cap->quality / (low_bitrate ? 400 : 200); + /* Lower limit (arbitrary, 1 KB) */ + if (size < 8192) + size = 8192; + /* Upper limit: 7/8 of the code buffers */ + if (size > ptr->total_code_vol * 7) + size = ptr->total_code_vol * 7; + + ptr->real_code_vol = size >> 3; /* in bytes */ + + /* the MBCVR is the *maximum* block volume, according to the + * JPEG ISO specs, this shouldn't be used, since that allows + * for the best encoding quality. So set it to it's max value + */ + reg = ptr->max_block_vol; + zr36060_write(ptr, ZR060_MBCVR, reg); + + return 0; +} + +/* additional control functions */ +static int zr36060_control(struct videocodec *codec, int type, int size, void *data) +{ + struct zr36060 *ptr = (struct zr36060 *)codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + int *ival = (int *)data; + + zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, + size); + + switch (type) { + case CODEC_G_STATUS: /* get last status */ + if (size != sizeof(int)) + return -EFAULT; + zr36060_read_status(ptr); + *ival = ptr->status; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = CODEC_MODE_BJPG; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != CODEC_MODE_BJPG) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + /* not needed, do nothing */ + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + *ival = ptr->total_code_vol; + break; + + case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + ptr->total_code_vol = *ival; + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + break; + + case CODEC_G_JPEG_SCALE: /* get scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + *ival = zr36060_read_scalefactor(ptr); + break; + + case CODEC_S_JPEG_SCALE: /* set scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + ptr->scalefact = *ival; + break; + + case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + *app = ptr->app; + break; + } + + case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + ptr->app = *app; + break; + } + + case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + *com = ptr->com; + break; + } + + case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + ptr->com = *com; + break; + } + + default: + return -EINVAL; + } + + return size; +} + +/* ========================================================================= + * Exit and unregister function: + * Deinitializes Zoran's JPEG processor + * ========================================================================= + */ +static int zr36060_unset(struct videocodec *codec) +{ + struct zr36060 *ptr = codec->data; + struct zoran *zr = videocodec_to_zoran(codec); + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36060_codecs--; + return 0; + } + + return -EFAULT; +} + +/* ========================================================================= + * Setup and registry function: + * Initializes Zoran's JPEG processor + * Also sets pixel size, average code size, mode (compr./decompr.) + * (the given size is determined by the processor with the video interface) + * ========================================================================= + */ +static int zr36060_setup(struct videocodec *codec) +{ + struct zr36060 *ptr; + struct zoran *zr = videocodec_to_zoran(codec); + int res; + + zrdev_dbg(zr, "zr36060: initializing MJPEG subsystem #%d.\n", + zr36060_codecs); + + if (zr36060_codecs == MAX_CODECS) { + zrdev_err(zr, "zr36060: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + codec->data = ptr; + if (!ptr) + return -ENOMEM; + + snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", zr36060_codecs); + ptr->num = zr36060_codecs++; + ptr->codec = codec; + + //testing + res = zr36060_basic_test(ptr); + if (res < 0) { + zr36060_unset(codec); + return res; + } + //final setup + memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); + memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); + + ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag (what is the difference?) */ + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 384; + ptr->height = 288; + ptr->total_code_vol = 16000; /* CHECKME */ + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ + ptr->scalefact = 0x100; + ptr->dri = 1; /* CHECKME, was 8 is 1 */ + + /* by default, no COM or APP markers - app should set those */ + ptr->com.len = 0; + ptr->app.appn = 0; + ptr->app.len = 0; + + zr36060_init(ptr); + + zrdev_info(zr, "%s: codec attached and running\n", ptr->name); + + return 0; +} + +static const struct videocodec zr36060_codec = { + .name = "zr36060", + .magic = 0L, // magic not used + .flags = + CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER | CODEC_FLAG_VFE, + .type = CODEC_TYPE_ZR36060, + .setup = zr36060_setup, // functionality + .unset = zr36060_unset, + .set_mode = zr36060_set_mode, + .set_video = zr36060_set_video, + .control = zr36060_control, + // others are not used +}; + +int zr36060_init_module(void) +{ + zr36060_codecs = 0; + return videocodec_register(&zr36060_codec); +} + +void zr36060_cleanup_module(void) +{ + if (zr36060_codecs) { + pr_debug("zr36060: something's wrong - %d codecs left somehow.\n", + zr36060_codecs); + } + + /* however, we can't just stay alive */ + videocodec_unregister(&zr36060_codec); +} diff --git a/drivers/media/pci/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h new file mode 100644 index 000000000000..75c88677a4bd --- /dev/null +++ b/drivers/media/pci/zoran/zr36060.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Zoran ZR36060 basic configuration functions - header file + * + * Copyright (C) 2002 Laurent Pinchart + */ + +#ifndef ZR36060_H +#define ZR36060_H + +#include "videocodec.h" + +/* data stored for each zoran jpeg codec chip */ +struct zr36060 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // last coder status + __u8 status; + // actual coder setup + int mode; + + __u16 width; + __u16 height; + + __u16 bitrate_ctrl; + + __u32 total_code_vol; + __u32 real_code_vol; + __u16 max_block_vol; + + __u8 h_samp_ratio[8]; + __u8 v_samp_ratio[8]; + __u16 scalefact; + __u16 dri; + + /* app/com marker data */ + struct jpeg_app_marker app; + struct jpeg_com_marker com; +}; + +/* ZR36060 register addresses */ +#define ZR060_LOAD 0x000 +#define ZR060_CFSR 0x001 +#define ZR060_CIR 0x002 +#define ZR060_CMR 0x003 +#define ZR060_MBZ 0x004 +#define ZR060_MBCVR 0x005 +#define ZR060_MER 0x006 +#define ZR060_IMR 0x007 +#define ZR060_ISR 0x008 +#define ZR060_TCV_NET_HI 0x009 +#define ZR060_TCV_NET_MH 0x00a +#define ZR060_TCV_NET_ML 0x00b +#define ZR060_TCV_NET_LO 0x00c +#define ZR060_TCV_DATA_HI 0x00d +#define ZR060_TCV_DATA_MH 0x00e +#define ZR060_TCV_DATA_ML 0x00f +#define ZR060_TCV_DATA_LO 0x010 +#define ZR060_SF_HI 0x011 +#define ZR060_SF_LO 0x012 +#define ZR060_AF_HI 0x013 +#define ZR060_AF_M 0x014 +#define ZR060_AF_LO 0x015 +#define ZR060_ACV_HI 0x016 +#define ZR060_ACV_MH 0x017 +#define ZR060_ACV_ML 0x018 +#define ZR060_ACV_LO 0x019 +#define ZR060_ACT_HI 0x01a +#define ZR060_ACT_MH 0x01b +#define ZR060_ACT_ML 0x01c +#define ZR060_ACT_LO 0x01d +#define ZR060_ACV_TURN_HI 0x01e +#define ZR060_ACV_TURN_MH 0x01f +#define ZR060_ACV_TURN_ML 0x020 +#define ZR060_ACV_TURN_LO 0x021 +#define ZR060_IDR_DEV 0x022 +#define ZR060_IDR_REV 0x023 +#define ZR060_TCR_HI 0x024 +#define ZR060_TCR_LO 0x025 +#define ZR060_VCR 0x030 +#define ZR060_VPR 0x031 +#define ZR060_SR 0x032 +#define ZR060_BCR_Y 0x033 +#define ZR060_BCR_U 0x034 +#define ZR060_BCR_V 0x035 +#define ZR060_SGR_VTOTAL_HI 0x036 +#define ZR060_SGR_VTOTAL_LO 0x037 +#define ZR060_SGR_HTOTAL_HI 0x038 +#define ZR060_SGR_HTOTAL_LO 0x039 +#define ZR060_SGR_VSYNC 0x03a +#define ZR060_SGR_HSYNC 0x03b +#define ZR060_SGR_BVSTART 0x03c +#define ZR060_SGR_BHSTART 0x03d +#define ZR060_SGR_BVEND_HI 0x03e +#define ZR060_SGR_BVEND_LO 0x03f +#define ZR060_SGR_BHEND_HI 0x040 +#define ZR060_SGR_BHEND_LO 0x041 +#define ZR060_AAR_VSTART_HI 0x042 +#define ZR060_AAR_VSTART_LO 0x043 +#define ZR060_AAR_VEND_HI 0x044 +#define ZR060_AAR_VEND_LO 0x045 +#define ZR060_AAR_HSTART_HI 0x046 +#define ZR060_AAR_HSTART_LO 0x047 +#define ZR060_AAR_HEND_HI 0x048 +#define ZR060_AAR_HEND_LO 0x049 +#define ZR060_SWR_VSTART_HI 0x04a +#define ZR060_SWR_VSTART_LO 0x04b +#define ZR060_SWR_VEND_HI 0x04c +#define ZR060_SWR_VEND_LO 0x04d +#define ZR060_SWR_HSTART_HI 0x04e +#define ZR060_SWR_HSTART_LO 0x04f +#define ZR060_SWR_HEND_HI 0x050 +#define ZR060_SWR_HEND_LO 0x051 + +#define ZR060_SOF_IDX 0x060 +#define ZR060_SOS_IDX 0x07a +#define ZR060_DRI_IDX 0x0c0 +#define ZR060_DQT_IDX 0x0cc +#define ZR060_DHT_IDX 0x1d4 +#define ZR060_APP_IDX 0x380 +#define ZR060_COM_IDX 0x3c0 + +/* ZR36060 LOAD register bits */ + +#define ZR060_LOAD_LOAD BIT(7) +#define ZR060_LOAD_SYNC_RST BIT(0) + +/* ZR36060 Code FIFO Status register bits */ + +#define ZR060_CFSR_BUSY BIT(7) +#define ZR060_CFSR_C_BUSY BIT(2) +#define ZR060_CFSR_CFIFO (3 << 0) + +/* ZR36060 Code Interface register */ + +#define ZR060_CIR_CODE16 BIT(7) +#define ZR060_CIR_ENDIAN BIT(6) +#define ZR060_CIR_CFIS BIT(2) +#define ZR060_CIR_CODE_MSTR BIT(0) + +/* ZR36060 Codec Mode register */ + +#define ZR060_CMR_COMP BIT(7) +#define ZR060_CMR_ATP BIT(6) +#define ZR060_CMR_PASS2 BIT(5) +#define ZR060_CMR_TLM BIT(4) +#define ZR060_CMR_BRB BIT(2) +#define ZR060_CMR_FSF BIT(1) + +/* ZR36060 Markers Enable register */ + +#define ZR060_MER_APP BIT(7) +#define ZR060_MER_COM BIT(6) +#define ZR060_MER_DRI BIT(5) +#define ZR060_MER_DQT BIT(4) +#define ZR060_MER_DHT BIT(3) + +/* ZR36060 Interrupt Mask register */ + +#define ZR060_IMR_EOAV BIT(3) +#define ZR060_IMR_EOI BIT(2) +#define ZR060_IMR_END BIT(1) +#define ZR060_IMR_DATA_ERR BIT(0) + +/* ZR36060 Interrupt Status register */ + +#define ZR060_ISR_PRO_CNT (3 << 6) +#define ZR060_ISR_EOAV BIT(3) +#define ZR060_ISR_EOI BIT(2) +#define ZR060_ISR_END BIT(1) +#define ZR060_ISR_DATA_ERR BIT(0) + +/* ZR36060 Video Control register */ + +#define ZR060_VCR_VIDEO8 BIT(7) +#define ZR060_VCR_RANGE BIT(6) +#define ZR060_VCR_FI_DET BIT(3) +#define ZR060_VCR_FI_VEDGE BIT(2) +#define ZR060_VCR_FI_EXT BIT(1) +#define ZR060_VCR_SYNC_MSTR BIT(0) + +/* ZR36060 Video Polarity register */ + +#define ZR060_VPR_VCLK_POL BIT(7) +#define ZR060_VPR_P_VAL_POL BIT(6) +#define ZR060_VPR_POE_POL BIT(5) +#define ZR060_VPR_S_IMG_POL BIT(4) +#define ZR060_VPR_BL_POL BIT(3) +#define ZR060_VPR_FI_POL BIT(2) +#define ZR060_VPR_HS_POL BIT(1) +#define ZR060_VPR_VS_POL BIT(0) + +/* ZR36060 Scaling register */ + +#define ZR060_SR_V_SCALE BIT(2) +#define ZR060_SR_H_SCALE2 BIT(0) +#define ZR060_SR_H_SCALE4 (2 << 0) + +int zr36060_init_module(void); +void zr36060_cleanup_module(void); +#endif /*fndef ZR36060_H */ diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 421ce9dbf44c..ce379cae01b9 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -44,6 +44,4 @@ source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" -source "drivers/staging/media/zoran/Kconfig" - endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 950e96f10aad..7ece57ca0403 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,5 +10,4 @@ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_DVB_AV7110) += av7110/ diff --git a/drivers/staging/media/zoran/Kconfig b/drivers/staging/media/zoran/Kconfig deleted file mode 100644 index 3fb3e27e04a8..000000000000 --- a/drivers/staging/media/zoran/Kconfig +++ /dev/null @@ -1,74 +0,0 @@ -config VIDEO_ZORAN - tristate "Zoran ZR36057/36067 Video For Linux (Deprecated)" - depends on PCI && I2C_ALGOBIT && VIDEO_DEV - depends on !ALPHA - depends on DEBUG_FS - select VIDEOBUF2_DMA_CONTIG - select VIDEO_ADV7170 if VIDEO_ZORAN_LML33R10 - select VIDEO_ADV7175 if VIDEO_ZORAN_DC10 || VIDEO_ZORAN_DC30 - select VIDEO_BT819 if VIDEO_ZORAN_LML33 - select VIDEO_BT856 if VIDEO_ZORAN_LML33 || VIDEO_ZORAN_AVS6EYES - select VIDEO_BT866 if VIDEO_ZORAN_AVS6EYES - select VIDEO_KS0127 if VIDEO_ZORAN_AVS6EYES - select VIDEO_SAA711X if VIDEO_ZORAN_BUZ || VIDEO_ZORAN_LML33R10 - select VIDEO_SAA7110 if VIDEO_ZORAN_DC10 - select VIDEO_SAA7185 if VIDEO_ZORAN_BUZ - select VIDEO_VPX3220 if VIDEO_ZORAN_DC30 - help - Say Y for support for MJPEG capture cards based on the Zoran - 36057/36067 PCI controller chipset. This includes the Iomega - Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is - a driver homepage at . For - more information, check . - - To compile this driver as a module, choose M here: the - module will be called zr36067. - -config VIDEO_ZORAN_DC30 - bool "Pinnacle/Miro DC30(+) support" - depends on VIDEO_ZORAN - help - Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback - card. This also supports really old DC10 cards based on the - zr36050 MJPEG codec and zr36016 VFE. - -config VIDEO_ZORAN_ZR36060 - bool "Zoran ZR36060" - depends on VIDEO_ZORAN - help - Say Y to support Zoran boards based on 36060 chips. - This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33 - and 33 R10 and AverMedia 6 boards. - -config VIDEO_ZORAN_BUZ - bool "Iomega Buz support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Iomega Buz MJPEG capture/playback card. - -config VIDEO_ZORAN_DC10 - bool "Pinnacle/Miro DC10(+) support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33 - bool "Linux Media Labs LML33 support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Linux Media Labs LML33 MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33R10 - bool "Linux Media Labs LML33R10 support" - depends on VIDEO_ZORAN_ZR36060 - help - support for the Linux Media Labs LML33R10 MJPEG capture/playback - card. - -config VIDEO_ZORAN_AVS6EYES - bool "AverMedia 6 Eyes support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/staging/media/zoran/Makefile b/drivers/staging/media/zoran/Makefile deleted file mode 100644 index 9603bac0195c..000000000000 --- a/drivers/staging/media/zoran/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -zr36067-objs := zoran_device.o \ - zoran_driver.o zoran_card.o videocodec.o - -obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o -zr36067-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o -zr36067-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o diff --git a/drivers/staging/media/zoran/TODO b/drivers/staging/media/zoran/TODO deleted file mode 100644 index 6992540d3e53..000000000000 --- a/drivers/staging/media/zoran/TODO +++ /dev/null @@ -1,19 +0,0 @@ - -How to test the zoran driver: -- RAW capture - mplayer tv:///dev/video0 -tv driver=v4l2 - -- MJPEG capture (compression) - mplayer tv:///dev/video0 -tv driver=v4l2:outfmt=mjpeg - TODO: need two test for both Dcim path - -- MJPEG play (decompression) - ffmpeg -i test.avi -vcodec mjpeg -an -f v4l2 /dev/video0 - Note: only recent ffmpeg has the ability of sending non-raw video via v4l2 - - The original way of sending video was via mplayer vo_zr/vo_zr2, but it does not compile - anymore and is a dead end (usage of some old private ffmpeg structures). - -TODO -- fix the v4l compliance "TRY_FMT cannot handle an invalid pixelformat" -- Filter JPEG data to made output work diff --git a/drivers/staging/media/zoran/videocodec.c b/drivers/staging/media/zoran/videocodec.c deleted file mode 100644 index 8efc5e06b0f7..000000000000 --- a/drivers/staging/media/zoran/videocodec.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr - */ - -#include -#include -#include -#include -#include - -#include "videocodec.h" - -struct attached_list { - struct videocodec *codec; - struct attached_list *next; -}; - -struct codec_list { - const struct videocodec *codec; - int attached; - struct attached_list *list; - struct codec_list *next; -}; - -static struct codec_list *codeclist_top; - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -struct videocodec *videocodec_attach(struct videocodec_master *master) -{ - struct codec_list *h = codeclist_top; - struct zoran *zr; - struct attached_list *a, *ptr; - struct videocodec *codec; - int res; - - if (!master) { - pr_err("%s: no data\n", __func__); - return NULL; - } - - zr = videocodec_master_to_zoran(master); - - zrdev_dbg(zr, "%s: '%s', flags %lx, magic %lx\n", __func__, - master->name, master->flags, master->magic); - - if (!h) { - zrdev_err(zr, "%s: no device available\n", __func__); - return NULL; - } - - while (h) { - // attach only if the slave has at least the flags - // expected by the master - if ((master->flags & h->codec->flags) == master->flags) { - zrdev_dbg(zr, "%s: try '%s'\n", __func__, h->codec->name); - - codec = kmemdup(h->codec, sizeof(struct videocodec), GFP_KERNEL); - if (!codec) - goto out_kfree; - - res = strlen(codec->name); - snprintf(codec->name + res, sizeof(codec->name) - res, "[%d]", h->attached); - codec->master_data = master; - res = codec->setup(codec); - if (res == 0) { - zrdev_dbg(zr, "%s: '%s'\n", __func__, codec->name); - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - goto out_kfree; - ptr->codec = codec; - - a = h->list; - if (!a) { - h->list = ptr; - zrdev_dbg(zr, "videocodec: first element\n"); - } else { - while (a->next) - a = a->next; // find end - a->next = ptr; - zrdev_dbg(zr, "videocodec: in after '%s'\n", - h->codec->name); - } - - h->attached += 1; - return codec; - } - kfree(codec); - } - h = h->next; - } - - zrdev_err(zr, "%s: no codec found!\n", __func__); - return NULL; - - out_kfree: - kfree(codec); - return NULL; -} - -int videocodec_detach(struct videocodec *codec) -{ - struct codec_list *h = codeclist_top; - struct zoran *zr; - struct attached_list *a, *prev; - int res; - - if (!codec) { - pr_err("%s: no data\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: '%s', type: %x, flags %lx, magic %lx\n", __func__, - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - zrdev_err(zr, "%s: no device left...\n", __func__); - return -ENXIO; - } - - while (h) { - a = h->list; - prev = NULL; - while (a) { - if (codec == a->codec) { - res = a->codec->unset(a->codec); - if (res >= 0) { - zrdev_dbg(zr, "%s: '%s'\n", __func__, - a->codec->name); - a->codec->master_data = NULL; - } else { - zrdev_err(zr, "%s: '%s'\n", __func__, a->codec->name); - a->codec->master_data = NULL; - } - if (!prev) { - h->list = a->next; - zrdev_dbg(zr, "videocodec: delete first\n"); - } else { - prev->next = a->next; - zrdev_dbg(zr, "videocodec: delete middle\n"); - } - kfree(a->codec); - kfree(a); - h->attached -= 1; - return 0; - } - prev = a; - a = a->next; - } - h = h->next; - } - - zrdev_err(zr, "%s: given codec not found!\n", __func__); - return -EINVAL; -} - -int videocodec_register(const struct videocodec *codec) -{ - struct codec_list *ptr, *h = codeclist_top; - struct zoran *zr; - - if (!codec) { - pr_err("%s: no data!\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran((struct videocodec *)codec); - - zrdev_dbg(zr, - "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - ptr->codec = codec; - - if (!h) { - codeclist_top = ptr; - zrdev_dbg(zr, "videocodec: hooked in as first element\n"); - } else { - while (h->next) - h = h->next; // find the end - h->next = ptr; - zrdev_dbg(zr, "videocodec: hooked in after '%s'\n", - h->codec->name); - } - - return 0; -} - -int videocodec_unregister(const struct videocodec *codec) -{ - struct codec_list *prev = NULL, *h = codeclist_top; - struct zoran *zr; - - if (!codec) { - pr_err("%s: no data!\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran((struct videocodec *)codec); - - zrdev_dbg(zr, - "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - zrdev_err(zr, "%s: no device left...\n", __func__); - return -ENXIO; - } - - while (h) { - if (codec == h->codec) { - if (h->attached) { - zrdev_err(zr, "videocodec: '%s' is used\n", - h->codec->name); - return -EBUSY; - } - zrdev_dbg(zr, "videocodec: unregister '%s' is ok.\n", - h->codec->name); - if (!prev) { - codeclist_top = h->next; - zrdev_dbg(zr, - "videocodec: delete first element\n"); - } else { - prev->next = h->next; - zrdev_dbg(zr, - "videocodec: delete middle element\n"); - } - kfree(h); - return 0; - } - prev = h; - h = h->next; - } - - zrdev_err(zr, "%s: given codec not found!\n", __func__); - return -EINVAL; -} - -int videocodec_debugfs_show(struct seq_file *m) -{ - struct codec_list *h = codeclist_top; - struct attached_list *a; - - seq_puts(m, "lave or attached aster name type flags magic "); - seq_puts(m, "(connected as)\n"); - - while (h) { - seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", - h->codec->name, h->codec->type, - h->codec->flags, h->codec->magic); - a = h->list; - while (a) { - seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", - a->codec->master_data->name, - a->codec->master_data->type, - a->codec->master_data->flags, - a->codec->master_data->magic, - a->codec->name); - a = a->next; - } - h = h->next; - } - - return 0; -} diff --git a/drivers/staging/media/zoran/videocodec.h b/drivers/staging/media/zoran/videocodec.h deleted file mode 100644 index 6b69f69667f9..000000000000 --- a/drivers/staging/media/zoran/videocodec.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr - */ - -/* =================== */ -/* general description */ -/* =================== */ - -/* - * Should ease the (re-)usage of drivers supporting cards with (different) - * video codecs. The codecs register to this module their functionality, - * and the processors (masters) can attach to them if they fit. - * - * The codecs are typically have a "strong" binding to their master - so I - * don't think it makes sense to have a full blown interfacing as with e.g. - * i2c. If you have an other opinion, let's discuss & implement it :-))) - * - * Usage: - * - * The slave has just to setup the videocodec structure and use two functions: - * videocodec_register(codecdata); - * videocodec_unregister(codecdata); - * The best is just calling them at module (de-)initialisation. - * - * The master sets up the structure videocodec_master and calls: - * codecdata=videocodec_attach(master_codecdata); - * videocodec_detach(codecdata); - * - * The slave is called during attach/detach via functions setup previously - * during register. At that time, the master_data pointer is set up - * and the slave can access any io registers of the master device (in the case - * the slave is bound to it). Otherwise it doesn't need this functions and - * therefor they may not be initialized. - * - * The other functions are just for convenience, as they are for sure used by - * most/all of the codecs. The last ones may be omitted, too. - * - * See the structure declaration below for more information and which data has - * to be set up for the master and the slave. - * - * ---------------------------------------------------------------------------- - * The master should have "knowledge" of the slave and vice versa. So the data - * structures sent to/from slave via set_data/get_data set_image/get_image are - * device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) - * ---------------------------------------------------------------------------- - */ - -/* ========================================== */ -/* description of the videocodec_io structure */ -/* ========================================== */ - -/* - * ==== master setup ==== - * name -> name of the device structure for reference and debugging - * master_data -> data ref. for the master (e.g. the zr36055,57,67) - * readreg -> ref. to read-fn from register (setup by master, used by slave) - * writereg -> ref. to write-fn to register (setup by master, used by slave) - * this two functions do the lowlevel I/O job - * - * ==== slave functionality setup ==== - * slave_data -> data ref. for the slave (e.g. the zr36050,60) - * check -> fn-ref. checks availability of an device, returns -EIO on failure or - * the type on success - * this makes espcecially sense if a driver module supports more than - * one codec which may be quite similar to access, nevertheless it - * is good for a first functionality check - * - * -- main functions you always need for compression/decompression -- - * - * set_mode -> this fn-ref. resets the entire codec, and sets up the mode - * with the last defined norm/size (or device default if not - * available) - it returns 0 if the mode is possible - * set_size -> this fn-ref. sets the norm and image size for - * compression/decompression (returns 0 on success) - * the norm param is defined in videodev2.h (V4L2_STD_*) - * - * additional setup may be available, too - but the codec should work with - * some default values even without this - * - * set_data -> sets device-specific data (tables, quality etc.) - * get_data -> query device-specific data (tables, quality etc.) - * - * if the device delivers interrupts, they may be setup/handled here - * setup_interrupt -> codec irq setup (not needed for 36050/60) - * handle_interrupt -> codec irq handling (not needed for 36050/60) - - * if the device delivers pictures, they may be handled here - * put_image -> puts image data to the codec (not needed for 36050/60) - * get_image -> gets image data from the codec (not needed for 36050/60) - * the calls include frame numbers and flags (even/odd/...) - * if needed and a flag which allows blocking until its ready - */ - -/* ============== */ -/* user interface */ -/* ============== */ - -/* - * Currently there is only a information display planned, as the layer - * is not visible for the user space at all. - * - * Information is available via procfs. The current entry is "/proc/videocodecs" - * but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. - * - * A example for such an output is: - * - * lave or attached aster name type flags magic (connected as) - * S zr36050 0002 0000d001 00000000 (TEMPLATE) - * M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) - * M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) - */ - -/* =============================================== */ -/* special defines for the videocodec_io structure */ -/* =============================================== */ - -#ifndef __LINUX_VIDEOCODEC_H -#define __LINUX_VIDEOCODEC_H - -#include -#include - -#define CODEC_DO_COMPRESSION 0 -#define CODEC_DO_EXPANSION 1 - -/* this are the current codec flags I think they are needed */ -/* -> type value in structure */ -#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec -#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec -#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec -#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec - // room for other types - -#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match -#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec -#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend -#define CODEC_FLAG_ENCODER 0x00004000L // compression capability -#define CODEC_FLAG_DECODER 0x00008000L // decompression capability -#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling -#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O - -/* a list of modes, some are just examples (is there any HW?) */ -#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG -#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG -#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 -#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 -#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 -#define CODEC_MODE_MSDIVX 0x0006 // MS DivX -#define CODEC_MODE_ODIVX 0x0007 // Open DivX -#define CODEC_MODE_WAVELET 0x0008 // Wavelet - -/* this are the current codec types I want to implement */ -/* -> type value in structure */ -#define CODEC_TYPE_NONE 0 -#define CODEC_TYPE_L64702 1 -#define CODEC_TYPE_ZR36050 2 -#define CODEC_TYPE_ZR36016 3 -#define CODEC_TYPE_ZR36060 4 - -/* the type of data may be enhanced by future implementations (data-fn.'s) */ -/* -> used in command */ -#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ -#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ -#define CODEC_G_CODEC_MODE 0x8001 -#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ -#define CODEC_G_VFE 0x8002 -#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ - -#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ -#define CODEC_G_JPEG_TDS_BYTE 0x8010 -#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ -#define CODEC_G_JPEG_SCALE 0x8011 -#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ -#define CODEC_G_JPEG_HDT_DATA 0x8018 -#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ -#define CODEC_G_JPEG_QDT_DATA 0x8019 -#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ -#define CODEC_G_JPEG_APP_DATA 0x801A -#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ -#define CODEC_G_JPEG_COM_DATA 0x801B - -#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ -#define CODEC_G_PRIVATE 0x9000 - -#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ - -/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ -/* -> used in get_image, put_image */ -#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ -#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ - -/* ========================= */ -/* the structures itself ... */ -/* ========================= */ - -struct vfe_polarity { - unsigned int vsync_pol:1; - unsigned int hsync_pol:1; - unsigned int field_pol:1; - unsigned int blank_pol:1; - unsigned int subimg_pol:1; - unsigned int poe_pol:1; - unsigned int pvalid_pol:1; - unsigned int vclk_pol:1; -}; - -struct vfe_settings { - __u32 x, y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ - __u16 quality; /* quality of the video */ -}; - -struct tvnorm { - u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; -}; - -struct jpeg_com_marker { - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct jpeg_app_marker { - int appn; /* number app segment */ - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct videocodec { - /* -- filled in by slave device during register -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* codec type */ - - /* -- these is filled in later during master device attach -- */ - - struct videocodec_master *master_data; - - /* -- these are filled in by the slave device during register -- */ - - void *data; /* private slave data */ - - /* attach/detach client functions (indirect call) */ - int (*setup)(struct videocodec *codec); - int (*unset)(struct videocodec *codec); - - /* main functions, every client needs them for sure! */ - // set compression or decompression (or freeze, stop, standby, etc) - int (*set_mode)(struct videocodec *codec, int mode); - // setup picture size and norm (for the codec's video frontend) - int (*set_video)(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol); - // other control commands, also mmap setup etc. - int (*control)(struct videocodec *codec, int type, int size, void *data); - - /* additional setup/query/processing (may be NULL pointer) */ - // interrupt setup / handling (for irq's delivered by master) - int (*setup_interrupt)(struct videocodec *codec, long mode); - int (*handle_interrupt)(struct videocodec *codec, int source, long flag); - // picture interface (if any) - long (*put_image)(struct videocodec *codec, int tr_type, int block, - long *fr_num, long *flag, long size, void *buf); - long (*get_image)(struct videocodec *codec, int tr_type, int block, - long *fr_num, long *flag, long size, void *buf); -}; - -struct videocodec_master { - /* -- filled in by master device for registration -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* master type */ - - void *data; /* private master data */ - - __u32 (*readreg)(struct videocodec *codec, __u16 reg); - void (*writereg)(struct videocodec *codec, __u16 reg, __u32 value); -}; - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -/* attach and detach commands for the master */ -// * master structure needs to be kmalloc'ed before calling attach -// and free'd after calling detach -// * returns pointer on success, NULL on failure -struct videocodec *videocodec_attach(struct videocodec_master *master); -// * 0 on success, <0 (errno) on failure -int videocodec_detach(struct videocodec *codec); - -/* register and unregister commands for the slaves */ -// * 0 on success, <0 (errno) on failure -int videocodec_register(const struct videocodec *codec); -// * 0 on success, <0 (errno) on failure -int videocodec_unregister(const struct videocodec *codec); - -/* the other calls are directly done via the videocodec structure! */ - -int videocodec_debugfs_show(struct seq_file *m); - -#include "zoran.h" -static inline struct zoran *videocodec_master_to_zoran(struct videocodec_master *master) -{ - struct zoran *zr = master->data; - - return zr; -} - -static inline struct zoran *videocodec_to_zoran(struct videocodec *codec) -{ - struct videocodec_master *master = codec->master_data; - - return videocodec_master_to_zoran(master); -} - -#endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h deleted file mode 100644 index 56340553b282..000000000000 --- a/drivers/staging/media/zoran/zoran.h +++ /dev/null @@ -1,328 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * zoran - Iomega Buz driver - * - * Copyright (C) 1999 Rainer Johanni - * - * based on - * - * zoran.0.0.3 Copyright (C) 1998 Dave Perks - * - * and - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - */ - -#ifndef _BUZ_H_ -#define _BUZ_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define ZR_NORM_PAL 0 -#define ZR_NORM_NTSC 1 -#define ZR_NORM_SECAM 2 - -struct zr_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vbuf; - struct list_head queue; -}; - -static inline struct zr_buffer *vb2_to_zr_buffer(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - return container_of(vbuf, struct zr_buffer, vbuf); -} - -#define ZORAN_NAME "ZORAN" /* name of the device */ - -#define ZR_DEVNAME(zr) ((zr)->name) - -#define BUZ_MAX_WIDTH (zr->timing->wa) -#define BUZ_MAX_HEIGHT (zr->timing->ha) -#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ -#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ - -#define BUZ_NUM_STAT_COM 4 -#define BUZ_MASK_STAT_COM 3 - -#define BUZ_MAX_INPUT 16 - -#include "zr36057.h" - -enum card_type { - UNKNOWN = -1, - - /* Pinnacle/Miro */ - DC10_OLD, /* DC30 like */ - DC10_NEW, /* DC10_PLUS like */ - DC10_PLUS, - DC30, - DC30_PLUS, - - /* Linux Media Labs */ - LML33, - LML33R10, - - /* Iomega */ - BUZ, - - /* AverMedia */ - AVS6EYES, - - /* total number of cards */ - NUM_CARDS -}; - -enum zoran_codec_mode { - BUZ_MODE_IDLE, /* nothing going on */ - BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ - BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ - BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ - BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ -}; - -enum zoran_map_mode { - ZORAN_MAP_MODE_NONE, - ZORAN_MAP_MODE_RAW, - ZORAN_MAP_MODE_JPG_REC, - ZORAN_MAP_MODE_JPG_PLAY, -}; - -enum gpio_type { - ZR_GPIO_JPEG_SLEEP = 0, - ZR_GPIO_JPEG_RESET, - ZR_GPIO_JPEG_FRAME, - ZR_GPIO_VID_DIR, - ZR_GPIO_VID_EN, - ZR_GPIO_VID_RESET, - ZR_GPIO_CLK_SEL1, - ZR_GPIO_CLK_SEL2, - ZR_GPIO_MAX, -}; - -enum gpcs_type { - GPCS_JPEG_RESET = 0, - GPCS_JPEG_START, - GPCS_MAX, -}; - -struct zoran_format { - char *name; - __u32 fourcc; - int colorspace; - int depth; - __u32 flags; - __u32 vfespfr; -}; - -/* flags */ -#define ZORAN_FORMAT_COMPRESSED BIT(0) -#define ZORAN_FORMAT_OVERLAY BIT(1) -#define ZORAN_FORMAT_CAPTURE BIT(2) -#define ZORAN_FORMAT_PLAYBACK BIT(3) - -/* v4l-capture settings */ -struct zoran_v4l_settings { - int width, height, bytesperline; /* capture size */ - const struct zoran_format *format; /* capture format */ -}; - -/* jpg-capture/-playback settings */ -struct zoran_jpg_settings { - /* this bit is used to set everything to default */ - int decimation; - /* capture decimation settings (tmp_dcm=1 means both fields) */ - int hor_dcm, ver_dcm, tmp_dcm; - /* field-settings (odd_even=1 (+tmp_dcm=1) means top-field-first) */ - int field_per_buff, odd_even; - /* crop settings (subframe capture) */ - int img_x, img_y, img_width, img_height; - /* JPEG-specific capture settings */ - struct v4l2_jpegcompression jpg_comp; -}; - -struct zoran; - -/* zoran_fh contains per-open() settings */ -struct zoran_fh { - struct v4l2_fh fh; - struct zoran *zr; -}; - -struct card_info { - enum card_type type; - char name[32]; - const char *i2c_decoder; /* i2c decoder device */ - const unsigned short *addrs_decoder; - const char *i2c_encoder; /* i2c encoder device */ - const unsigned short *addrs_encoder; - u16 video_vfe, video_codec; /* videocodec types */ - u16 audio_chip; /* audio type */ - - int inputs; /* number of video inputs */ - struct input { - int muxsel; - char name[32]; - } input[BUZ_MAX_INPUT]; - - v4l2_std_id norms; - const struct tvnorm *tvn[3]; /* supported TV norms */ - - u32 jpeg_int; /* JPEG interrupt */ - u32 vsync_int; /* VSYNC interrupt */ - s8 gpio[ZR_GPIO_MAX]; - u8 gpcs[GPCS_MAX]; - - struct vfe_polarity vfe_pol; - u8 gpio_pol[ZR_GPIO_MAX]; - - /* is the /GWS line connected? */ - u8 gws_not_connected; - - /* avs6eyes mux setting */ - u8 input_mux; - - void (*init)(struct zoran *zr); -}; - -struct zoran { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct video_device *video_dev; - struct vb2_queue vq; - - struct i2c_adapter i2c_adapter; /* */ - struct i2c_algo_bit_data i2c_algo; /* */ - u32 i2cbr; - - struct v4l2_subdev *decoder; /* video decoder sub-device */ - struct v4l2_subdev *encoder; /* video encoder sub-device */ - - struct videocodec *codec; /* video codec */ - struct videocodec *vfe; /* video front end */ - - struct mutex lock; /* file ops serialize lock */ - - u8 initialized; /* flag if zoran has been correctly initialized */ - struct card_info card; - const struct tvnorm *timing; - - unsigned short id; /* number of this device */ - char name[32]; /* name of this device */ - struct pci_dev *pci_dev; /* PCI device */ - unsigned char revision; /* revision of zr36057 */ - unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ - - spinlock_t spinlock; /* Spinlock */ - - /* Video for Linux parameters */ - int input; /* card's norm and input */ - v4l2_std_id norm; - - /* Current buffer params */ - unsigned int buffer_size; - - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - - /* Buz MJPEG parameters */ - enum zoran_codec_mode codec_mode; /* status of codec */ - struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ - - /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ - /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ - /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ - unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ - unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ - unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ - unsigned long jpg_que_tail; /* Index of last buffer in queue */ - unsigned long jpg_seq_num; /* count of frames since grab/play started */ - unsigned long jpg_err_seq; /* last seq_num before error */ - unsigned long jpg_err_shift; - unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ - unsigned long vbseq; - - /* zr36057's code buffer table */ - /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ - __le32 *stat_com; - - /* Additional stuff for testing */ - unsigned int ghost_int; - int intr_counter_GIRQ1; - int intr_counter_GIRQ0; - int intr_counter_cod_rep_irq; - int intr_counter_jpeg_rep_irq; - int field_counter; - int irq1_in; - int irq1_out; - int jpeg_in; - int jpeg_out; - int JPEG_0; - int JPEG_1; - int end_event_missed; - int jpeg_missed; - int jpeg_error; - int num_errors; - int jpeg_max_missed; - int jpeg_min_missed; - unsigned int prepared; - unsigned int queued; - - u32 last_isr; - unsigned long frame_num; - int running; - int buf_in_reserve; - - dma_addr_t p_sc; - __le32 *stat_comb; - dma_addr_t p_scb; - enum zoran_map_mode map_mode; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - struct zr_buffer *inuse[BUZ_NUM_STAT_COM * 2]; - struct dentry *dbgfs_dir; -}; - -static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct zoran, v4l2_dev); -} - -/* - * There was something called _ALPHA_BUZ that used the PCI address instead of - * the kernel iomapped address for btread/btwrite. - */ -#define btwrite(dat, adr) writel((dat), zr->zr36057_mem + (adr)) -#define btread(adr) readl(zr->zr36057_mem + (adr)) - -#define btand(dat, adr) btwrite((dat) & btread(adr), (adr)) -#define btor(dat, adr) btwrite((dat) | btread(adr), (adr)) -#define btaor(dat, mask, adr) btwrite((dat) | ((mask) & btread(adr)), (adr)) - -#endif - -/* - * Debugging macros - */ -#define zrdev_dbg(zr, format, args...) \ - pci_dbg((zr)->pci_dev, format, ##args) \ - -#define zrdev_err(zr, format, args...) \ - pci_err((zr)->pci_dev, format, ##args) \ - -#define zrdev_info(zr, format, args...) \ - pci_info((zr)->pci_dev, format, ##args) \ - -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir); -void zoran_queue_exit(struct zoran *zr); -int zr_set_buf(struct zoran *zr); diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c deleted file mode 100644 index 3975fc1b2ee3..000000000000 --- a/drivers/staging/media/zoran/zoran_card.c +++ /dev/null @@ -1,1440 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_card.h" -#include "zoran_device.h" -#include "zr36016.h" -#include "zr36050.h" -#include "zr36060.h" - -extern const struct zoran_format zoran_formats[]; - -static int card[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; -module_param_array(card, int, NULL, 0444); -MODULE_PARM_DESC(card, "Card type"); - -/* Default input and video norm at startup of the driver. */ - -static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ -module_param(default_input, uint, 0444); -MODULE_PARM_DESC(default_input, - "Default input (0=Composite, 1=S-Video, 2=Internal)"); - -static int default_mux = 1; /* 6 Eyes input selection */ -module_param(default_mux, int, 0644); -MODULE_PARM_DESC(default_mux, - "Default 6 Eyes mux setting (Input selection)"); - -static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ -module_param(default_norm, int, 0444); -MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); - -/* /dev/videoN, -1 for autodetect */ -static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; -module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); - -/* 1=Pass through TV signal when device is not used */ -/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ -int pass_through; -module_param(pass_through, int, 0644); -MODULE_PARM_DESC(pass_through, - "Pass TV signal through to TV-out when idling"); - -int zr36067_debug = 1; -module_param_named(debug, zr36067_debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-5)"); - -#define ZORAN_VERSION "0.10.1" - -MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); -MODULE_AUTHOR("Serguei Miridonov"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(ZORAN_VERSION); - -#define ZR_DEVICE(subven, subdev, data) { \ - .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ - .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } - -static const struct pci_device_id zr36067_pci_tbl[] = { - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10_PLUS), - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30_PLUS), - ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), - ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), - ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), - {0} -}; -MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); - -static unsigned int zoran_num; /* number of cards found */ - -/* videocodec bus functions ZR36060 */ -static u32 zr36060_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || - post_office_write(zr, 0, 2, reg & 0xff)) - return -1; - - data = post_office_read(zr, 0, 3) & 0xff; - return data; -} - -static void zr36060_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || - post_office_write(zr, 0, 2, reg & 0xff)) - return; - - post_office_write(zr, 0, 3, val & 0xff); -} - -/* videocodec bus functions ZR36050 */ -static u32 zr36050_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES - return -1; - - data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read - return data; -} - -static void zr36050_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES - return; - - post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data -} - -/* videocodec bus functions ZR36016 */ -static u32 zr36016_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr)) - return -1; - - data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read - return data; -} - -/* hack for in zoran_device.c */ -void zr36016_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr)) - return; - - post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data -} - -/* - * Board specific information - */ - -static void dc10_init(struct zoran *zr) -{ - /* Pixel clock selection */ - GPIO(zr, 4, 0); - GPIO(zr, 5, 1); - /* Enable the video bus sync signals */ - GPIO(zr, 7, 0); -} - -static void dc10plus_init(struct zoran *zr) -{ -} - -static void buz_init(struct zoran *zr) -{ - /* some stuff from Iomega */ - pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); - pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); - pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); -} - -static void lml33_init(struct zoran *zr) -{ - GPIO(zr, 2, 1); // Set Composite input/output -} - -static void avs6eyes_init(struct zoran *zr) -{ - // AverMedia 6-Eyes original driver by Christer Weinigel - - // Lifted straight from Christer's old driver and - // modified slightly by Martin Samuelsson. - - int mux = default_mux; /* 1 = BT866, 7 = VID1 */ - - GPIO(zr, 4, 1); /* Bt866 SLEEP on */ - udelay(2); - - GPIO(zr, 0, 1); /* ZR36060 /RESET on */ - GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ - GPIO(zr, 2, mux & 1); /* MUX S0 */ - GPIO(zr, 3, 0); /* /FRAME on */ - GPIO(zr, 4, 0); /* Bt866 SLEEP off */ - GPIO(zr, 5, mux & 2); /* MUX S1 */ - GPIO(zr, 6, 0); /* ? */ - GPIO(zr, 7, mux & 4); /* MUX S2 */ -} - -static const char *codecid_to_modulename(u16 codecid) -{ - const char *name = NULL; - - switch (codecid) { - case CODEC_TYPE_ZR36060: - name = "zr36060"; - break; - case CODEC_TYPE_ZR36050: - name = "zr36050"; - break; - case CODEC_TYPE_ZR36016: - name = "zr36016"; - break; - } - - return name; -} - -static int codec_init(struct zoran *zr, u16 codecid) -{ - switch (codecid) { - case CODEC_TYPE_ZR36060: -#ifdef CONFIG_VIDEO_ZORAN_ZR36060 - return zr36060_init_module(); -#else - pci_err(zr->pci_dev, "ZR36060 support is not enabled\n"); - return -EINVAL; -#endif - break; - case CODEC_TYPE_ZR36050: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - return zr36050_init_module(); -#else - pci_err(zr->pci_dev, "ZR36050 support is not enabled\n"); - return -EINVAL; -#endif - break; - case CODEC_TYPE_ZR36016: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - return zr36016_init_module(); -#else - pci_err(zr->pci_dev, "ZR36016 support is not enabled\n"); - return -EINVAL; -#endif - break; - } - - pci_err(zr->pci_dev, "unknown codec id %x\n", codecid); - return -EINVAL; -} - -static void codec_exit(struct zoran *zr, u16 codecid) -{ - switch (codecid) { - case CODEC_TYPE_ZR36060: -#ifdef CONFIG_VIDEO_ZORAN_ZR36060 - zr36060_cleanup_module(); -#endif - break; - case CODEC_TYPE_ZR36050: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - zr36050_cleanup_module(); -#endif - break; - case CODEC_TYPE_ZR36016: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - zr36016_cleanup_module(); -#endif - break; - } -} - -static int videocodec_init(struct zoran *zr) -{ - const char *codec_name, *vfe_name; - int result; - - codec_name = codecid_to_modulename(zr->card.video_codec); - if (codec_name) { - result = codec_init(zr, zr->card.video_codec); - if (result < 0) { - pci_err(zr->pci_dev, "failed to load video codec %s: %d\n", - codec_name, result); - return result; - } - } - vfe_name = codecid_to_modulename(zr->card.video_vfe); - if (vfe_name) { - result = codec_init(zr, zr->card.video_vfe); - if (result < 0) { - pci_err(zr->pci_dev, "failed to load video vfe %s: %d\n", - vfe_name, result); - if (codec_name) - codec_exit(zr, zr->card.video_codec); - return result; - } - } - return 0; -} - -static void videocodec_exit(struct zoran *zr) -{ - if (zr->card.video_codec != CODEC_TYPE_NONE) - codec_exit(zr, zr->card.video_codec); - if (zr->card.video_vfe != CODEC_TYPE_NONE) - codec_exit(zr, zr->card.video_vfe); -} - -static const struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; -static const struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; -static const struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; - -static const struct tvnorm f50ccir601_lml33 = { 864, 720, 75 + 34, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_lml33 = { 858, 720, 57 + 34, 788, 525, 480, 16 }; - -/* The DC10 (57/16/50) uses VActive as HSync, so h_start must be 0 */ -static const struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; -static const struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; - -/* - * FIXME: I cannot swap U and V in saa7114, so i do one pixel left shift in zoran (75 -> 74) - * (Maxim Yevtyushkin ) - */ -static const struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74 + 54, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56 + 54, 788, 525, 480, 16 }; - -/* - * FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I copy Maxim's left - * shift hack for the 6 Eyes. - * - * Christer's driver used the unshifted norms, though... - * /Sam - */ -static const struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; - -static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; -static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; -static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; -static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; -static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; -static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; -static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; -static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; - -static struct card_info zoran_cards[NUM_CARDS] = { - { - .type = DC10_OLD, - .name = "DC10(old)", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC10_NEW, - .name = "DC10(new)", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel}, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1}, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC10_PLUS, - .name = "DC10_PLUS", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel - }, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1 }, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC30, - .name = "DC30", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC30_PLUS, - .name = "DC30_PLUS", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = LML33, - .name = "LML33", - .i2c_decoder = "bt819a", - .addrs_decoder = bt819_addrs, - .i2c_encoder = "bt856", - .addrs_encoder = bt856_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_lml33, - &f60ccir601_lml33, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = LML33R10, - .name = "LML33R10", - .i2c_decoder = "saa7114", - .addrs_decoder = saa7114_addrs, - .i2c_encoder = "adv7170", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_lm33r10, - &f60ccir601_lm33r10, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = BUZ, - .name = "Buz", - .i2c_decoder = "saa7111", - .addrs_decoder = saa7111_addrs, - .i2c_encoder = "saa7185", - .addrs_encoder = saa7185_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 3, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50ccir601, - &f60ccir601, - &f50ccir601 - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &buz_init, - }, { - .type = AVS6EYES, - .name = "6-Eyes", - /* - * AverMedia chose not to brand the 6-Eyes. Thus it can't be - * autodetected, and requires card=x. - */ - .i2c_decoder = "ks0127", - .addrs_decoder = ks0127_addrs, - .i2c_encoder = "bt866", - .addrs_encoder = bt866_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 10, - .input = { - { 0, "Composite 1" }, - { 1, "Composite 2" }, - { 2, "Composite 3" }, - { 4, "Composite 4" }, - { 5, "Composite 5" }, - { 6, "Composite 6" }, - { 8, "S-Video 1" }, - { 9, "S-Video 2" }, - {10, "S-Video 3" }, - {15, "YCbCr" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_avs6eyes, - &f60ccir601_avs6eyes, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam - .gpcs = { 3, 1 }, // Validity unknown /Sam - .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam - .gws_not_connected = 1, - .input_mux = 1, - .init = &avs6eyes_init, - } - -}; - -/* - * I2C functions - */ -/* software I2C functions */ -static int zoran_i2c_getsda(void *data) -{ - struct zoran *zr = (struct zoran *)data; - - return (btread(ZR36057_I2CBR) >> 1) & 1; -} - -static int zoran_i2c_getscl(void *data) -{ - struct zoran *zr = (struct zoran *)data; - - return btread(ZR36057_I2CBR) & 1; -} - -static void zoran_i2c_setsda(void *data, int state) -{ - struct zoran *zr = (struct zoran *)data; - - if (state) - zr->i2cbr |= 2; - else - zr->i2cbr &= ~2; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static void zoran_i2c_setscl(void *data, int state) -{ - struct zoran *zr = (struct zoran *)data; - - if (state) - zr->i2cbr |= 1; - else - zr->i2cbr &= ~1; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { - .setsda = zoran_i2c_setsda, - .setscl = zoran_i2c_setscl, - .getsda = zoran_i2c_getsda, - .getscl = zoran_i2c_getscl, - .udelay = 10, - .timeout = 100, -}; - -static int zoran_register_i2c(struct zoran *zr) -{ - zr->i2c_algo = zoran_i2c_bit_data_template; - zr->i2c_algo.data = zr; - strscpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), - sizeof(zr->i2c_adapter.name)); - i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); - zr->i2c_adapter.algo_data = &zr->i2c_algo; - zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; - return i2c_bit_add_bus(&zr->i2c_adapter); -} - -static void zoran_unregister_i2c(struct zoran *zr) -{ - i2c_del_adapter(&zr->i2c_adapter); -} - -/* Check a zoran_params struct for correctness, insert default params */ -int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, int try) -{ - int err = 0, err0 = 0; - - pci_dbg(zr->pci_dev, "%s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", - __func__, settings->decimation, settings->hor_dcm, - settings->ver_dcm, settings->tmp_dcm); - pci_dbg(zr->pci_dev, "%s - x: %d, y: %d, w: %d, y: %d\n", __func__, - settings->img_x, settings->img_y, - settings->img_width, settings->img_height); - /* Check decimation, set default values for decimation = 1, 2, 4 */ - switch (settings->decimation) { - case 1: - - settings->hor_dcm = 1; - settings->ver_dcm = 1; - settings->tmp_dcm = 1; - settings->field_per_buff = 2; - settings->img_x = 0; - settings->img_y = 0; - settings->img_width = BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 2: - - settings->hor_dcm = 2; - settings->ver_dcm = 1; - settings->tmp_dcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 4: - - if (zr->card.type == DC10_NEW) { - pci_dbg(zr->pci_dev, - "%s - HDec by 4 is not supported on the DC10\n", - __func__); - err0++; - break; - } - - settings->hor_dcm = 4; - settings->ver_dcm = 2; - settings->tmp_dcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 0: - - /* We have to check the data the user has set */ - - if (settings->hor_dcm != 1 && settings->hor_dcm != 2 && - (zr->card.type == DC10_NEW || settings->hor_dcm != 4)) { - settings->hor_dcm = clamp(settings->hor_dcm, 1, 2); - err0++; - } - if (settings->ver_dcm != 1 && settings->ver_dcm != 2) { - settings->ver_dcm = clamp(settings->ver_dcm, 1, 2); - err0++; - } - if (settings->tmp_dcm != 1 && settings->tmp_dcm != 2) { - settings->tmp_dcm = clamp(settings->tmp_dcm, 1, 2); - err0++; - } - if (settings->field_per_buff != 1 && - settings->field_per_buff != 2) { - settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); - err0++; - } - if (settings->img_x < 0) { - settings->img_x = 0; - err0++; - } - if (settings->img_y < 0) { - settings->img_y = 0; - err0++; - } - if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { - settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); - err0++; - } - if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); - err0++; - } - if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { - settings->img_x = BUZ_MAX_WIDTH - settings->img_width; - err0++; - } - if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; - err0++; - } - if (settings->img_width % (16 * settings->hor_dcm) != 0) { - settings->img_width -= settings->img_width % (16 * settings->hor_dcm); - if (settings->img_width == 0) - settings->img_width = 16 * settings->hor_dcm; - err0++; - } - if (settings->img_height % (8 * settings->ver_dcm) != 0) { - settings->img_height -= settings->img_height % (8 * settings->ver_dcm); - if (settings->img_height == 0) - settings->img_height = 8 * settings->ver_dcm; - err0++; - } - - if (!try && err0) { - pci_err(zr->pci_dev, "%s - error in params for decimation = 0\n", __func__); - err++; - } - break; - default: - pci_err(zr->pci_dev, "%s - decimation = %d, must be 0, 1, 2 or 4\n", - __func__, settings->decimation); - err++; - break; - } - - if (settings->jpg_comp.quality > 100) - settings->jpg_comp.quality = 100; - if (settings->jpg_comp.quality < 5) - settings->jpg_comp.quality = 5; - if (settings->jpg_comp.APPn < 0) - settings->jpg_comp.APPn = 0; - if (settings->jpg_comp.APPn > 15) - settings->jpg_comp.APPn = 15; - if (settings->jpg_comp.APP_len < 0) - settings->jpg_comp.APP_len = 0; - if (settings->jpg_comp.APP_len > 60) - settings->jpg_comp.APP_len = 60; - if (settings->jpg_comp.COM_len < 0) - settings->jpg_comp.COM_len = 0; - if (settings->jpg_comp.COM_len > 60) - settings->jpg_comp.COM_len = 60; - if (err) - return -EINVAL; - return 0; -} - -static int zoran_init_video_device(struct zoran *zr, struct video_device *video_dev, int dir) -{ - int err; - - /* Now add the template and register the device unit. */ - *video_dev = zoran_template; - video_dev->v4l2_dev = &zr->v4l2_dev; - video_dev->lock = &zr->lock; - video_dev->device_caps = V4L2_CAP_STREAMING | dir; - - strscpy(video_dev->name, ZR_DEVNAME(zr), sizeof(video_dev->name)); - video_dev->vfl_dir = VFL_DIR_RX; - zoran_queue_init(zr, &zr->vq, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - err = video_register_device(video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); - if (err < 0) - return err; - video_set_drvdata(video_dev, zr); - return 0; -} - -static void zoran_exit_video_devices(struct zoran *zr) -{ - video_unregister_device(zr->video_dev); - kfree(zr->video_dev); -} - -static int zoran_init_video_devices(struct zoran *zr) -{ - int err; - - zr->video_dev = video_device_alloc(); - if (!zr->video_dev) - return -ENOMEM; - - err = zoran_init_video_device(zr, zr->video_dev, V4L2_CAP_VIDEO_CAPTURE); - if (err) - kfree(zr->video_dev); - return err; -} - -/* - * v4l2_device_unregister() will care about removing zr->encoder/zr->decoder - * via v4l2_i2c_subdev_unregister() - */ -static int zoran_i2c_init(struct zoran *zr) -{ - int err; - - pci_info(zr->pci_dev, "Initializing i2c bus...\n"); - - err = zoran_register_i2c(zr); - if (err) { - pci_err(zr->pci_dev, "%s - cannot initialize i2c bus\n", __func__); - return err; - } - - zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_decoder, 0, - zr->card.addrs_decoder); - if (!zr->decoder) { - pci_err(zr->pci_dev, "Fail to get decoder %s\n", zr->card.i2c_decoder); - err = -EINVAL; - goto error_decoder; - } - - if (zr->card.i2c_encoder) { - zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_encoder, 0, - zr->card.addrs_encoder); - if (!zr->encoder) { - pci_err(zr->pci_dev, "Fail to get encoder %s\n", zr->card.i2c_encoder); - err = -EINVAL; - goto error_decoder; - } - } - return 0; - -error_decoder: - zoran_unregister_i2c(zr); - return err; -} - -static void zoran_i2c_exit(struct zoran *zr) -{ - zoran_unregister_i2c(zr); -} - -void zoran_open_init_params(struct zoran *zr) -{ - int i; - - zr->v4l_settings.width = 192; - zr->v4l_settings.height = 144; - zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ - zr->v4l_settings.bytesperline = zr->v4l_settings.width * - ((zr->v4l_settings.format->depth + 7) / 8); - - /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ - zr->jpg_settings.decimation = 1; - zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ - if (zr->card.type != BUZ) - zr->jpg_settings.odd_even = 1; - else - zr->jpg_settings.odd_even = 0; - zr->jpg_settings.jpg_comp.APPn = 0; - zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ - memset(zr->jpg_settings.jpg_comp.APP_data, 0, - sizeof(zr->jpg_settings.jpg_comp.APP_data)); - zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ - memset(zr->jpg_settings.jpg_comp.COM_data, 0, - sizeof(zr->jpg_settings.jpg_comp.COM_data)); - zr->jpg_settings.jpg_comp.jpeg_markers = - V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; - i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); - if (i) - pci_err(zr->pci_dev, "%s internal error\n", __func__); - - zr->buffer_size = zr->v4l_settings.bytesperline * zr->v4l_settings.height; - - clear_interrupt_counters(zr); -} - -static int zr36057_init(struct zoran *zr) -{ - int j, err; - - pci_info(zr->pci_dev, "initializing card[%d]\n", zr->id); - - /* Avoid nonsense settings from user for default input/norm */ - if (default_norm < 0 || default_norm > 2) - default_norm = 0; - if (default_norm == 0) { - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - } else if (default_norm == 1) { - zr->norm = V4L2_STD_NTSC; - zr->timing = zr->card.tvn[ZR_NORM_NTSC]; - } else { - zr->norm = V4L2_STD_SECAM; - zr->timing = zr->card.tvn[ZR_NORM_SECAM]; - } - if (!zr->timing) { - pci_warn(zr->pci_dev, - "%s - default TV standard not supported by hardware. PAL will be used.\n", - __func__); - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - } - - if (default_input > zr->card.inputs - 1) { - pci_warn(zr->pci_dev, "default_input value %d out of range (0-%d)\n", - default_input, zr->card.inputs - 1); - default_input = 0; - } - zr->input = default_input; - - /* default setup (will be repeated at every open) */ - zoran_open_init_params(zr); - - /* allocate memory *before* doing anything to the hardware in case allocation fails */ - zr->stat_com = dma_alloc_coherent(&zr->pci_dev->dev, - BUZ_NUM_STAT_COM * sizeof(u32), - &zr->p_sc, GFP_KERNEL); - if (!zr->stat_com) - return -ENOMEM; - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) - zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ - - zr->stat_comb = dma_alloc_coherent(&zr->pci_dev->dev, - BUZ_NUM_STAT_COM * sizeof(u32) * 2, - &zr->p_scb, GFP_KERNEL); - if (!zr->stat_comb) { - err = -ENOMEM; - goto exit_statcom; - } - - err = zoran_init_video_devices(zr); - if (err) - goto exit_statcomb; - - zoran_init_hardware(zr); - if (!pass_through) { - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - - zr->initialized = 1; - return 0; - -exit_statcomb: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, - zr->stat_comb, zr->p_scb); -exit_statcom: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), - zr->stat_com, zr->p_sc); - return err; -} - -static void zoran_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct zoran *zr = to_zoran(v4l2_dev); - - if (!zr->initialized) - goto exit_free; - - debugfs_remove_recursive(zr->dbgfs_dir); - - zoran_queue_exit(zr); - - /* unregister videocodec bus */ - if (zr->codec) - videocodec_detach(zr->codec); - if (zr->vfe) - videocodec_detach(zr->vfe); - videocodec_exit(zr); - - /* unregister i2c bus */ - zoran_i2c_exit(zr); - /* disable PCI bus-mastering */ - zoran_set_pci_master(zr, 0); - /* put chip into reset */ - btwrite(0, ZR36057_SPGPPCR); - pci_free_irq(zr->pci_dev, 0, zr); - /* unmap and free memory */ - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), - zr->stat_com, zr->p_sc); - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, - zr->stat_comb, zr->p_scb); - pci_release_regions(pdev); - pci_disable_device(zr->pci_dev); - zoran_exit_video_devices(zr); -exit_free: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); -} - -void zoran_vdev_release(struct video_device *vdev) -{ - kfree(vdev); -} - -static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr, - int type) -{ - struct videocodec_master *m = NULL; - - m = devm_kmalloc(&zr->pci_dev->dev, sizeof(*m), GFP_KERNEL); - if (!m) - return m; - - /* - * magic and type are unused for master struct. Makes sense only at codec structs. - * In the past, .type were initialized to the old V4L1 .hardware value, - * as VID_HARDWARE_ZR36067 - */ - m->magic = 0L; - m->type = 0; - - m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strscpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); - m->data = zr; - - switch (type) { - case CODEC_TYPE_ZR36060: - m->readreg = zr36060_read; - m->writereg = zr36060_write; - m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; - break; - case CODEC_TYPE_ZR36050: - m->readreg = zr36050_read; - m->writereg = zr36050_write; - m->flags |= CODEC_FLAG_JPEG; - break; - case CODEC_TYPE_ZR36016: - m->readreg = zr36016_read; - m->writereg = zr36016_write; - m->flags |= CODEC_FLAG_VFE; - break; - } - - return m; -} - -static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct zoran *zr = to_zoran(sd->v4l2_dev); - - /* - * Bt819 needs to reset its FIFO buffer using #FRST pin and - * LML33 card uses GPIO(7) for that. - */ - if (cmd == BT819_FIFO_RESET_LOW) - GPIO(zr, 7, 0); - else if (cmd == BT819_FIFO_RESET_HIGH) - GPIO(zr, 7, 1); -} - -static int zoran_video_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct zoran *zr = container_of(ctrl->handler, struct zoran, hdl); - - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - zr->jpg_settings.jpg_comp.quality = ctrl->val; - return zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops zoran_video_ctrl_ops = { - .s_ctrl = zoran_video_set_ctrl, -}; - -static int zoran_debugfs_show(struct seq_file *seq, void *v) -{ - struct zoran *zr = seq->private; - - seq_printf(seq, "Running mode %x\n", zr->running); - seq_printf(seq, "Codec mode %x\n", zr->codec_mode); - seq_printf(seq, "Norm %llx\n", zr->norm); - seq_printf(seq, "Input %d\n", zr->input); - seq_printf(seq, "Buffersize %d\n", zr->buffer_size); - - seq_printf(seq, "V4L width %dx%d\n", zr->v4l_settings.width, zr->v4l_settings.height); - seq_printf(seq, "V4L bytesperline %d\n", zr->v4l_settings.bytesperline); - - seq_printf(seq, "JPG decimation %u\n", zr->jpg_settings.decimation); - seq_printf(seq, "JPG hor_dcm %u\n", zr->jpg_settings.hor_dcm); - seq_printf(seq, "JPG ver_dcm %u\n", zr->jpg_settings.ver_dcm); - seq_printf(seq, "JPG tmp_dcm %u\n", zr->jpg_settings.tmp_dcm); - seq_printf(seq, "JPG odd_even %u\n", zr->jpg_settings.odd_even); - seq_printf(seq, "JPG crop %dx%d %d %d\n", - zr->jpg_settings.img_x, - zr->jpg_settings.img_y, - zr->jpg_settings.img_width, - zr->jpg_settings.img_height); - - seq_printf(seq, "Prepared %u\n", zr->prepared); - seq_printf(seq, "Queued %u\n", zr->queued); - - videocodec_debugfs_show(seq); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(zoran_debugfs); - -/* - * Scan for a Buz card (actually for the PCI controller ZR36057), - * request the irq and map the io memory - */ -static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - unsigned char latency, need_latency; - struct zoran *zr; - int result; - struct videocodec_master *master_vfe = NULL; - struct videocodec_master *master_codec = NULL; - int card_num; - unsigned int nr; - int err; - - pci_info(pdev, "Zoran MJPEG board driver version %s\n", ZORAN_VERSION); - - /* some mainboards might not do PCI-PCI data transfer well */ - if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) - pci_warn(pdev, "%s: chipset does not support reliable PCI-PCI DMA\n", - ZORAN_NAME); - - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) - return err; - err = vb2_dma_contig_set_max_seg_size(&pdev->dev, U32_MAX); - if (err) - return err; - - nr = zoran_num++; - if (nr >= BUZ_MAX) { - pci_err(pdev, "driver limited to %d card(s) maximum\n", BUZ_MAX); - return -ENOENT; - } - - zr = devm_kzalloc(&pdev->dev, sizeof(*zr), GFP_KERNEL); - if (!zr) - return -ENOMEM; - - zr->v4l2_dev.notify = zoran_subdev_notify; - if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) - goto zr_free_mem; - zr->pci_dev = pdev; - zr->id = nr; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); - if (v4l2_ctrl_handler_init(&zr->hdl, 10)) - goto zr_unreg; - zr->v4l2_dev.ctrl_handler = &zr->hdl; - v4l2_ctrl_new_std(&zr->hdl, &zoran_video_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, - 100, 1, 50); - spin_lock_init(&zr->spinlock); - mutex_init(&zr->lock); - if (pci_enable_device(pdev)) - goto zr_unreg; - zr->revision = zr->pci_dev->revision; - - pci_info(zr->pci_dev, "Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n", - zr->revision < 2 ? '5' : '6', zr->revision, - zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0)); - if (zr->revision >= 2) - pci_info(zr->pci_dev, "Subsystem vendor=0x%04x id=0x%04x\n", - zr->pci_dev->subsystem_vendor, zr->pci_dev->subsystem_device); - - /* Use auto-detected card type? */ - if (card[nr] == -1) { - if (zr->revision < 2) { - pci_err(pdev, "No card type specified, please use the card=X module parameter\n"); - pci_err(pdev, "It is not possible to auto-detect ZR36057 based cards\n"); - goto zr_unreg; - } - - card_num = ent->driver_data; - if (card_num >= NUM_CARDS) { - pci_err(pdev, "Unknown card, try specifying card=X module parameter\n"); - goto zr_unreg; - } - pci_info(zr->pci_dev, "%s() - card %s detected\n", __func__, - zoran_cards[card_num].name); - } else { - card_num = card[nr]; - if (card_num >= NUM_CARDS || card_num < 0) { - pci_err(pdev, "User specified card type %d out of range (0 .. %d)\n", - card_num, NUM_CARDS - 1); - goto zr_unreg; - } - } - - /* - * even though we make this a non pointer and thus - * theoretically allow for making changes to this struct - * on a per-individual card basis at runtime, this is - * strongly discouraged. This structure is intended to - * keep general card information, no settings or anything - */ - zr->card = zoran_cards[card_num]; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "%s[%u]", - zr->card.name, zr->id); - - err = pci_request_regions(pdev, ZR_DEVNAME(zr)); - if (err) - goto zr_unreg; - - zr->zr36057_mem = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (!zr->zr36057_mem) { - pci_err(pdev, "%s() - ioremap failed\n", __func__); - goto zr_pci_release; - } - - result = pci_request_irq(pdev, 0, zoran_irq, NULL, zr, ZR_DEVNAME(zr)); - if (result < 0) { - if (result == -EINVAL) { - pci_err(pdev, "%s - bad IRQ number or handler\n", __func__); - } else if (result == -EBUSY) { - pci_err(pdev, "%s - IRQ %d busy, change your PnP config in BIOS\n", - __func__, zr->pci_dev->irq); - } else { - pci_err(pdev, "%s - cannot assign IRQ, error code %d\n", __func__, result); - } - goto zr_pci_release; - } - - /* set PCI latency timer */ - pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, - &latency); - need_latency = zr->revision > 1 ? 32 : 48; - if (latency != need_latency) { - pci_info(zr->pci_dev, "Changing PCI latency from %d to %d\n", - latency, need_latency); - pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); - } - - zr36057_restart(zr); - - err = zoran_i2c_init(zr); - if (err) - goto zr_free_irq; - - pci_info(zr->pci_dev, "Initializing videocodec bus...\n"); - err = videocodec_init(zr); - if (err) - goto zr_unreg_i2c; - - /* reset JPEG codec */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_reset(zr); - /* video bus enabled */ - /* display codec revision */ - if (zr->card.video_codec != 0) { - master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); - if (!master_codec) - goto zr_unreg_videocodec; - zr->codec = videocodec_attach(master_codec); - if (!zr->codec) { - pci_err(pdev, "%s - no codec found\n", __func__); - goto zr_unreg_videocodec; - } - if (zr->codec->type != zr->card.video_codec) { - pci_err(pdev, "%s - wrong codec\n", __func__); - goto zr_unreg_videocodec; - } - } - if (zr->card.video_vfe != 0) { - master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe); - if (!master_vfe) - goto zr_detach_codec; - zr->vfe = videocodec_attach(master_vfe); - if (!zr->vfe) { - pci_err(pdev, "%s - no VFE found\n", __func__); - goto zr_detach_codec; - } - if (zr->vfe->type != zr->card.video_vfe) { - pci_err(pdev, "%s = wrong VFE\n", __func__); - goto zr_detach_vfe; - } - } - - /* take care of Natoma chipset and a revision 1 zr36057 */ - if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) - pci_info(zr->pci_dev, "ZR36057/Natoma bug, max. buffer size is 128K\n"); - - if (zr36057_init(zr) < 0) - goto zr_detach_vfe; - - zr->map_mode = ZORAN_MAP_MODE_RAW; - - zr->dbgfs_dir = debugfs_create_dir(ZR_DEVNAME(zr), NULL); - debugfs_create_file("debug", 0444, zr->dbgfs_dir, zr, - &zoran_debugfs_fops); - return 0; - -zr_detach_vfe: - videocodec_detach(zr->vfe); -zr_detach_codec: - videocodec_detach(zr->codec); -zr_unreg_videocodec: - videocodec_exit(zr); -zr_unreg_i2c: - zoran_i2c_exit(zr); -zr_free_irq: - btwrite(0, ZR36057_SPGPPCR); - pci_free_irq(zr->pci_dev, 0, zr); -zr_pci_release: - pci_release_regions(pdev); -zr_unreg: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); -zr_free_mem: - - return -ENODEV; -} - -static struct pci_driver zoran_driver = { - .name = "zr36067", - .id_table = zr36067_pci_tbl, - .probe = zoran_probe, - .remove = zoran_remove, -}; - -module_pci_driver(zoran_driver); diff --git a/drivers/staging/media/zoran/zoran_card.h b/drivers/staging/media/zoran/zoran_card.h deleted file mode 100644 index 518cb426b446..000000000000 --- a/drivers/staging/media/zoran/zoran_card.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov - */ - -#ifndef __ZORAN_CARD_H__ -#define __ZORAN_CARD_H__ - -extern int zr36067_debug; - -/* Anybody who uses more than four? */ -#define BUZ_MAX 4 - -extern const struct video_device zoran_template; - -int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, int try); -void zoran_open_init_params(struct zoran *zr); -void zoran_vdev_release(struct video_device *vdev); - -void zr36016_write(struct videocodec *codec, u16 reg, u32 val); - -#endif /* __ZORAN_CARD_H__ */ diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c deleted file mode 100644 index 31f049b55529..000000000000 --- a/drivers/staging/media/zoran/zoran_device.c +++ /dev/null @@ -1,956 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles device access (PCI/I2C/codec/...) - * - * Copyright (C) 2000 Serguei Miridonov - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - -#define IRQ_MASK (ZR36057_ISR_GIRQ0 | \ - ZR36057_ISR_GIRQ1 | \ - ZR36057_ISR_JPEG_REP_IRQ) - -static bool lml33dpath; /* default = 0 - * 1 will use digital path in capture - * mode instead of analog. It can be - * used for picture adjustments using - * tool like xawtv while watching image - * on TV monitor connected to the output. - * However, due to absence of 75 Ohm - * load on Bt819 input, there will be - * some image imperfections - */ - -module_param(lml33dpath, bool, 0644); -MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)"); - -/* - * initialize video front end - */ -static void zr36057_init_vfe(struct zoran *zr) -{ - u32 reg; - - reg = btread(ZR36057_VFESPFR); - reg |= ZR36057_VFESPFR_LITTLE_ENDIAN; - reg &= ~ZR36057_VFESPFR_VCLK_POL; - reg |= ZR36057_VFESPFR_EXT_FL; - reg |= ZR36057_VFESPFR_TOP_FIELD; - btwrite(reg, ZR36057_VFESPFR); - reg = btread(ZR36057_VDCR); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_TRITON; - else - reg |= ZR36057_VDCR_TRITON; - btwrite(reg, ZR36057_VDCR); -} - -/* - * General Purpose I/O and Guest bus access - */ - -/* - * This is a bit tricky. When a board lacks a GPIO function, the corresponding - * GPIO bit number in the card_info structure is set to 0. - */ - -void GPIO(struct zoran *zr, int bit, unsigned int value) -{ - u32 reg; - u32 mask; - - /* Make sure the bit number is legal - * A bit number of -1 (lacking) gives a mask of 0, - * making it harmless - */ - mask = (1 << (24 + bit)) & 0xff000000; - reg = btread(ZR36057_GPPGCR1) & ~mask; - if (value) - reg |= mask; - - btwrite(reg, ZR36057_GPPGCR1); - udelay(1); -} - -/* - * Wait til post office is no longer busy - */ - -int post_office_wait(struct zoran *zr) -{ - u32 por; - - while ((por = btread(ZR36057_POR)) & ZR36057_POR_PO_PEN) { - /* wait for something to happen */ - /* TODO add timeout */ - } - if ((por & ZR36057_POR_PO_TIME) && !zr->card.gws_not_connected) { - /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ - pci_info(zr->pci_dev, "pop timeout %08x\n", por); - return -1; - } - - return 0; -} - -int post_office_write(struct zoran *zr, unsigned int guest, - unsigned int reg, unsigned int value) -{ - u32 por; - - por = - ZR36057_POR_PO_DIR | ZR36057_POR_PO_TIME | ((guest & 7) << 20) | - ((reg & 7) << 16) | (value & 0xFF); - btwrite(por, ZR36057_POR); - - return post_office_wait(zr); -} - -int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg) -{ - u32 por; - - por = ZR36057_POR_PO_TIME | ((guest & 7) << 20) | ((reg & 7) << 16); - btwrite(por, ZR36057_POR); - if (post_office_wait(zr) < 0) - return -1; - - return btread(ZR36057_POR) & 0xFF; -} - -/* - * JPEG Codec access - */ - -void jpeg_codec_sleep(struct zoran *zr, int sleep) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); - if (!sleep) { - pci_dbg(zr->pci_dev, "%s() - wake GPIO=0x%08x\n", - __func__, btread(ZR36057_GPPGCR1)); - usleep_range(500, 1000); - } else { - pci_dbg(zr->pci_dev, "%s() - sleep GPIO=0x%08x\n", - __func__, btread(ZR36057_GPPGCR1)); - udelay(2); - } -} - -int jpeg_codec_reset(struct zoran *zr) -{ - /* Take the codec out of sleep */ - jpeg_codec_sleep(zr, 0); - - if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { - post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, - 0); - udelay(2); - } else { - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0); - udelay(2); - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1); - udelay(2); - } - - return 0; -} - -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the ZR36057 manual in front of - * you [AC]. - */ -static void zr36057_adjust_vfe(struct zoran *zr, enum zoran_codec_mode mode) -{ - u32 reg; - - switch (mode) { - case BUZ_MODE_MOTION_DECOMPRESS: - btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if ((reg & (1 << 10)) && zr->card.type != LML33R10) - reg += ((1 << 10) | 1); - - btwrite(reg, ZR36057_VFEHCR); - break; - case BUZ_MODE_MOTION_COMPRESS: - case BUZ_MODE_IDLE: - default: - if ((zr->norm & V4L2_STD_NTSC) || - (zr->card.type == LML33R10 && - (zr->norm & V4L2_STD_PAL))) - btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - else - btor(ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if (!(reg & (1 << 10)) && zr->card.type != LML33R10) - reg -= ((1 << 10) | 1); - - btwrite(reg, ZR36057_VFEHCR); - break; - } -} - -/* - * set geometry - */ - -static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, - const struct zoran_format *format) -{ - const struct tvnorm *tvn; - unsigned int h_start, h_end, v_start, v_end; - unsigned int disp_mode; - unsigned int vid_win_wid, vid_win_ht; - unsigned int hcrop1, hcrop2, vcrop1, vcrop2; - unsigned int wa, we, ha, he; - unsigned int X, Y, hor_dcm, ver_dcm; - u32 reg; - - tvn = zr->timing; - - wa = tvn->wa; - ha = tvn->ha; - - pci_dbg(zr->pci_dev, "set_vfe() - width = %d, height = %d\n", video_width, video_height); - - if (video_width < BUZ_MIN_WIDTH || - video_height < BUZ_MIN_HEIGHT || - video_width > wa || video_height > ha) { - pci_err(zr->pci_dev, "set_vfe: w=%d h=%d not valid\n", video_width, video_height); - return; - } - - /**** zr36057 ****/ - - /* horizontal */ - vid_win_wid = video_width; - X = DIV_ROUND_UP(vid_win_wid * 64, tvn->wa); - we = (vid_win_wid * 64) / X; - hor_dcm = 64 - X; - hcrop1 = 2 * ((tvn->wa - we) / 4); - hcrop2 = tvn->wa - we - hcrop1; - h_start = tvn->h_start ? tvn->h_start : 1; - /* (Ronald) Original comment: - * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" - * this is false. It inverses chroma values on the LML33R10 (so Cr - * suddenly is shown as Cb and reverse, really cool effect if you - * want to see blue faces, not useful otherwise). So don't use |1. - * However, the DC10 has '0' as h_start, but does need |1, so we - * use a dirty check... - */ - h_end = h_start + tvn->wa - 1; - h_start += hcrop1; - h_end -= hcrop2; - reg = ((h_start & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_START) - | ((h_end & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_END); - if (zr->card.vfe_pol.hsync_pol) - reg |= ZR36057_VFEHCR_HS_POL; - btwrite(reg, ZR36057_VFEHCR); - - /* Vertical */ - disp_mode = !(video_height > BUZ_MAX_HEIGHT / 2); - vid_win_ht = disp_mode ? video_height : video_height / 2; - Y = DIV_ROUND_UP(vid_win_ht * 64 * 2, tvn->ha); - he = (vid_win_ht * 64) / Y; - ver_dcm = 64 - Y; - vcrop1 = (tvn->ha / 2 - he) / 2; - vcrop2 = tvn->ha / 2 - he - vcrop1; - v_start = tvn->v_start; - // FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP - v_end = v_start + tvn->ha / 2; // - 1; - v_start += vcrop1; - v_end -= vcrop2; - reg = ((v_start & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_START) - | ((v_end & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_END); - if (zr->card.vfe_pol.vsync_pol) - reg |= ZR36057_VFEVCR_VS_POL; - btwrite(reg, ZR36057_VFEVCR); - - /* scaler and pixel format */ - reg = 0; - reg |= (hor_dcm << ZR36057_VFESPFR_HOR_DCM); - reg |= (ver_dcm << ZR36057_VFESPFR_VER_DCM); - reg |= (disp_mode << ZR36057_VFESPFR_DISP_MODE); - /* - * RJ: I don't know, why the following has to be the opposite - * of the corresponding ZR36060 setting, but only this way - * we get the correct colors when uncompressing to the screen - */ - //reg |= ZR36057_VFESPFR_VCLK_POL; - /* RJ: Don't know if that is needed for NTSC also */ - if (!(zr->norm & V4L2_STD_NTSC)) - reg |= ZR36057_VFESPFR_EXT_FL; // NEEDED!!!!!!! Wolfgang - reg |= ZR36057_VFESPFR_TOP_FIELD; - if (hor_dcm >= 48) - reg |= 3 << ZR36057_VFESPFR_H_FILTER; /* 5 tap filter */ - else if (hor_dcm >= 32) - reg |= 2 << ZR36057_VFESPFR_H_FILTER; /* 4 tap filter */ - else if (hor_dcm >= 16) - reg |= 1 << ZR36057_VFESPFR_H_FILTER; /* 3 tap filter */ - - reg |= format->vfespfr; - btwrite(reg, ZR36057_VFESPFR); - - /* display configuration */ - reg = (16 << ZR36057_VDCR_MIN_PIX) - | (vid_win_ht << ZR36057_VDCR_VID_WIN_HT) - | (vid_win_wid << ZR36057_VDCR_VID_WIN_WID); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_TRITON; - else - reg |= ZR36057_VDCR_TRITON; - btwrite(reg, ZR36057_VDCR); - - zr36057_adjust_vfe(zr, zr->codec_mode); -} - -/* Enable/Disable uncompressed memory grabbing of the 36057 */ -void zr36057_set_memgrab(struct zoran *zr, int mode) -{ - if (mode) { - /* We only check SnapShot and not FrameGrab here. SnapShot==1 - * means a capture is already in progress, but FrameGrab==1 - * doesn't necessary mean that. It's more correct to say a 1 - * to 0 transition indicates a capture completed. If a - * capture is pending when capturing is tuned off, FrameGrab - * will be stuck at 1 until capturing is turned back on. - */ - if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) - pci_warn(zr->pci_dev, "%s(1) with SnapShot on!?\n", __func__); - - /* switch on VSync interrupts */ - btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts - btor(zr->card.vsync_int, ZR36057_ICR); // SW - - /* enable SnapShot */ - btor(ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); - - /* Set zr36057 video front end and enable video */ - zr36057_set_vfe(zr, zr->v4l_settings.width, - zr->v4l_settings.height, - zr->v4l_settings.format); - } else { - /* switch off VSync interrupts */ - btand(~zr->card.vsync_int, ZR36057_ICR); // SW - - /* re-enable grabbing to screen if it was running */ - btand(~ZR36057_VDCR_VID_EN, ZR36057_VDCR); - btand(~ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); - } -} - -/***************************************************************************** - * * - * Set up the Buz-specific MJPEG part * - * * - *****************************************************************************/ - -static inline void set_frame(struct zoran *zr, int val) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val); -} - -static void set_videobus_dir(struct zoran *zr, int val) -{ - switch (zr->card.type) { - case LML33: - case LML33R10: - if (!lml33dpath) - GPIO(zr, 5, val); - else - GPIO(zr, 5, 1); - break; - default: - GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR], - zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val); - break; - } -} - -static void init_jpeg_queue(struct zoran *zr) -{ - int i; - - /* re-initialize DMA ring stuff */ - zr->jpg_que_head = 0; - zr->jpg_dma_head = 0; - zr->jpg_dma_tail = 0; - zr->jpg_que_tail = 0; - zr->jpg_seq_num = 0; - zr->jpeg_error = 0; - zr->num_errors = 0; - zr->jpg_err_seq = 0; - zr->jpg_err_shift = 0; - zr->jpg_queued_num = 0; - for (i = 0; i < BUZ_NUM_STAT_COM; i++) - zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ -} - -static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - const struct tvnorm *tvn; - u32 reg; - - tvn = zr->timing; - - /* assert P_Reset, disable code transfer, deassert Active */ - btwrite(0, ZR36057_JPC); - - /* MJPEG compression mode */ - switch (mode) { - case BUZ_MODE_MOTION_COMPRESS: - default: - reg = ZR36057_JMC_MJPG_CMP_MODE; - break; - - case BUZ_MODE_MOTION_DECOMPRESS: - reg = ZR36057_JMC_MJPG_EXP_MODE; - reg |= ZR36057_JMC_SYNC_MSTR; - /* RJ: The following is experimental - improves the output to screen */ - //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM - break; - - case BUZ_MODE_STILL_COMPRESS: - reg = ZR36057_JMC_JPG_CMP_MODE; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - reg = ZR36057_JMC_JPG_EXP_MODE; - break; - } - reg |= ZR36057_JMC_JPG; - if (zr->jpg_settings.field_per_buff == 1) - reg |= ZR36057_JMC_FLD_PER_BUFF; - btwrite(reg, ZR36057_JMC); - - /* vertical */ - btor(ZR36057_VFEVCR_VS_POL, ZR36057_VFEVCR); - reg = (6 << ZR36057_VSP_VSYNC_SIZE) | - (tvn->ht << ZR36057_VSP_FRM_TOT); - btwrite(reg, ZR36057_VSP); - reg = ((zr->jpg_settings.img_y + tvn->v_start) << ZR36057_FVAP_NAY) | - (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); - btwrite(reg, ZR36057_FVAP); - - /* horizontal */ - if (zr->card.vfe_pol.hsync_pol) - btor(ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); - else - btand(~ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); - reg = ((tvn->h_sync_start) << ZR36057_HSP_HSYNC_START) | - (tvn->wt << ZR36057_HSP_LINE_TOT); - btwrite(reg, ZR36057_HSP); - reg = ((zr->jpg_settings.img_x + - tvn->h_start + 4) << ZR36057_FHAP_NAX) | - (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); - btwrite(reg, ZR36057_FHAP); - - /* field process parameters */ - if (zr->jpg_settings.odd_even) - reg = ZR36057_FPP_ODD_EVEN; - else - reg = 0; - - btwrite(reg, ZR36057_FPP); - - /* Set proper VCLK Polarity, else colors will be wrong during playback */ - //btor(ZR36057_VFESPFR_VCLK_POL, ZR36057_VFESPFR); - - /* code base address */ - btwrite(zr->p_sc, ZR36057_JCBA); - - /* FIFO threshold (FIFO is 160. double words) */ - /* NOTE: decimal values here */ - switch (mode) { - case BUZ_MODE_STILL_COMPRESS: - case BUZ_MODE_MOTION_COMPRESS: - if (zr->card.type != BUZ) - reg = 140; - else - reg = 60; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - case BUZ_MODE_MOTION_DECOMPRESS: - reg = 20; - break; - - default: - reg = 80; - break; - } - btwrite(reg, ZR36057_JCFT); - zr36057_adjust_vfe(zr, mode); -} - -void clear_interrupt_counters(struct zoran *zr) -{ - zr->intr_counter_GIRQ1 = 0; - zr->intr_counter_GIRQ0 = 0; - zr->intr_counter_cod_rep_irq = 0; - zr->intr_counter_jpeg_rep_irq = 0; - zr->field_counter = 0; - zr->irq1_in = 0; - zr->irq1_out = 0; - zr->jpeg_in = 0; - zr->jpeg_out = 0; - zr->JPEG_0 = 0; - zr->JPEG_1 = 0; - zr->end_event_missed = 0; - zr->jpeg_missed = 0; - zr->jpeg_max_missed = 0; - zr->jpeg_min_missed = 0x7fffffff; -} - -static u32 count_reset_interrupt(struct zoran *zr) -{ - u32 isr; - - isr = btread(ZR36057_ISR) & 0x78000000; - if (isr) { - if (isr & ZR36057_ISR_GIRQ1) { - btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); - zr->intr_counter_GIRQ1++; - } - if (isr & ZR36057_ISR_GIRQ0) { - btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); - zr->intr_counter_GIRQ0++; - } - if (isr & ZR36057_ISR_COD_REP_IRQ) { - btwrite(ZR36057_ISR_COD_REP_IRQ, ZR36057_ISR); - zr->intr_counter_cod_rep_irq++; - } - if (isr & ZR36057_ISR_JPEG_REP_IRQ) { - btwrite(ZR36057_ISR_JPEG_REP_IRQ, ZR36057_ISR); - zr->intr_counter_jpeg_rep_irq++; - } - } - return isr; -} - -void jpeg_start(struct zoran *zr) -{ - int reg; - - zr->frame_num = 0; - - /* deassert P_reset, disable code transfer, deassert Active */ - btwrite(ZR36057_JPC_P_RESET, ZR36057_JPC); - /* stop flushing the internal code buffer */ - btand(~ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); - /* enable code transfer */ - btor(ZR36057_JPC_COD_TRNS_EN, ZR36057_JPC); - - /* clear IRQs */ - btwrite(IRQ_MASK, ZR36057_ISR); - /* enable the JPEG IRQs */ - btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ | ZR36057_ICR_INT_PIN_EN, - ZR36057_ICR); - - set_frame(zr, 0); // \FRAME - - /* set the JPEG codec guest ID */ - reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPE_GUEST_ID) | - (0 << ZR36057_JCGI_JPE_GUEST_REG); - btwrite(reg, ZR36057_JCGI); - - if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && - zr->card.video_codec == CODEC_TYPE_ZR36050) { - /* Enable processing on the ZR36016 */ - if (zr->vfe) - zr36016_write(zr->vfe, 0, 1); - - /* load the address of the GO register in the ZR36050 latch */ - post_office_write(zr, 0, 0, 0); - } - - /* assert Active */ - btor(ZR36057_JPC_ACTIVE, ZR36057_JPC); - - /* enable the Go generation */ - btor(ZR36057_JMC_GO_EN, ZR36057_JMC); - usleep_range(30, 100); - - set_frame(zr, 1); // /FRAME -} - -void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - struct vfe_settings cap; - int field_size = zr->buffer_size / zr->jpg_settings.field_per_buff; - - zr->codec_mode = mode; - - cap.x = zr->jpg_settings.img_x; - cap.y = zr->jpg_settings.img_y; - cap.width = zr->jpg_settings.img_width; - cap.height = zr->jpg_settings.img_height; - cap.decimation = - zr->jpg_settings.hor_dcm | (zr->jpg_settings.ver_dcm << 8); - cap.quality = zr->jpg_settings.jpg_comp.quality; - - switch (mode) { - case BUZ_MODE_MOTION_COMPRESS: { - struct jpeg_app_marker app; - struct jpeg_com_marker com; - - /* In motion compress mode, the decoder output must be enabled, and - * the video bus direction set to input. - */ - set_videobus_dir(zr, 0); - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - - /* set JPEG app/com marker */ - app.appn = zr->jpg_settings.jpg_comp.APPn; - app.len = zr->jpg_settings.jpg_comp.APP_len; - memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, - sizeof(struct jpeg_app_marker), &app); - - com.len = zr->jpg_settings.jpg_comp.COM_len; - memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, - sizeof(struct jpeg_com_marker), &com); - - /* Setup the JPEG codec */ - zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); - - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); - } - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - pci_dbg(zr->pci_dev, "enable_jpg(MOTION_COMPRESS)\n"); - break; - } - - case BUZ_MODE_MOTION_DECOMPRESS: - /* In motion decompression mode, the decoder output must be disabled, and - * the video bus direction set to output. - */ - decoder_call(zr, video, s_stream, 0); - set_videobus_dir(zr, 1); - encoder_call(zr, video, s_routing, 1, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); - } - /* Setup the JPEG codec */ - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - pci_dbg(zr->pci_dev, "enable_jpg(MOTION_DECOMPRESS)\n"); - break; - - case BUZ_MODE_IDLE: - default: - /* shut down processing */ - btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ), - ZR36057_ICR); - btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ, - ZR36057_ISR); - btand(~ZR36057_JMC_GO_EN, ZR36057_JMC); // \Go_en - - msleep(50); - - set_videobus_dir(zr, 0); - set_frame(zr, 1); // /FRAME - btor(ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); // /CFlush - btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active - btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); - btand(~ZR36057_JMC_SYNC_MSTR, ZR36057_JMC); - jpeg_codec_reset(zr); - jpeg_codec_sleep(zr, 1); - zr36057_adjust_vfe(zr, mode); - - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - pci_dbg(zr->pci_dev, "enable_jpg(IDLE)\n"); - break; - } -} - -/* when this is called the spinlock must be held */ -void zoran_feed_stat_com(struct zoran *zr) -{ - /* move frames from pending queue to DMA */ - - int i, max_stat_com; - struct zr_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr = 0; - unsigned long flags; - unsigned long payload; - - max_stat_com = - (zr->jpg_settings.tmp_dcm == - 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com) { - buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); - if (!buf) { - pci_err(zr->pci_dev, "No buffer available to queue\n"); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return; - } - list_del(&buf->queue); - zr->buf_in_reserve--; - vbuf = &buf->vbuf; - vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; - phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - payload = vb2_get_plane_payload(&vbuf->vb2_buf, 0); - if (payload == 0) - payload = zr->buffer_size; - if (zr->jpg_settings.tmp_dcm == 1) { - /* fill 1 stat_com entry */ - i = (zr->jpg_dma_head - - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); - zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); - zr->inuse[i] = buf; - zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); - } else { - /* fill 2 stat_com entries */ - i = ((zr->jpg_dma_head - - zr->jpg_err_shift) & 1) * 2; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); - zr->stat_com[i + 1] = cpu_to_le32(zr->p_scb + i * 2 * 4); - - zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); - zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); - - zr->inuse[i] = buf; - zr->inuse[i + 1] = NULL; - } - zr->jpg_dma_head++; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_queued_num++; -} - -/* when this is called the spinlock must be held */ -static void zoran_reap_stat_com(struct zoran *zr) -{ - /* move frames from DMA queue to done queue */ - - int i; - u32 stat_com; - unsigned int seq; - unsigned int dif; - unsigned long flags; - struct zr_buffer *buf; - unsigned int size = 0; - u32 fcnt; - - /* - * In motion decompress we don't have a hardware frame counter, - * we just count the interrupts here - */ - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_seq_num++; - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while (zr->jpg_dma_tail < zr->jpg_dma_head) { - if (zr->jpg_settings.tmp_dcm == 1) - i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; - - stat_com = le32_to_cpu(zr->stat_com[i]); - if ((stat_com & 1) == 0) { - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return; - } - - fcnt = (stat_com & GENMASK(31, 24)) >> 24; - size = (stat_com & GENMASK(22, 1)) >> 1; - - buf = zr->inuse[i]; - if (!buf) { - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - pci_err(zr->pci_dev, "No buffer at slot %d\n", i); - return; - } - buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, size); - - /* update sequence number with the help of the counter in stat_com */ - seq = (fcnt + zr->jpg_err_seq) & 0xff; - dif = (seq - zr->jpg_seq_num) & 0xff; - zr->jpg_seq_num += dif; - } - buf->vbuf.sequence = zr->jpg_settings.tmp_dcm == - 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; - zr->inuse[i] = NULL; - if (zr->jpg_settings.tmp_dcm != 1) - buf->vbuf.field = zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; - else - buf->vbuf.field = zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); - - zr->jpg_dma_tail++; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); -} - -irqreturn_t zoran_irq(int irq, void *dev_id) -{ - struct zoran *zr = dev_id; - u32 stat, astat; - - stat = count_reset_interrupt(zr); - astat = stat & IRQ_MASK; - if (astat & zr->card.vsync_int) { - if (zr->running == ZORAN_MAP_MODE_RAW) { - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) == 0) - pci_warn(zr->pci_dev, "BuzIRQ with SnapShot off ???\n"); - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FRAME_GRAB) == 0) - zr_set_buf(zr); - return IRQ_HANDLED; - } - if (astat & ZR36057_ISR_JPEG_REP_IRQ) { - if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && - zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { - pci_err(zr->pci_dev, "JPG IRQ when not in good mode\n"); - return IRQ_HANDLED; - } - zr->frame_num++; - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - return IRQ_HANDLED; - } - /* unused interrupts */ - } - zr->ghost_int++; - return IRQ_HANDLED; -} - -void zoran_set_pci_master(struct zoran *zr, int set_master) -{ - if (set_master) { - pci_set_master(zr->pci_dev); - } else { - u16 command; - - pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); - } -} - -void zoran_init_hardware(struct zoran *zr) -{ - /* Enable bus-mastering */ - zoran_set_pci_master(zr, 1); - - /* Initialize the board */ - if (zr->card.init) - zr->card.init(zr); - - decoder_call(zr, core, init, 0); - decoder_call(zr, video, s_std, zr->norm); - decoder_call(zr, video, s_routing, - zr->card.input[zr->input].muxsel, 0, 0); - - encoder_call(zr, core, init, 0); - encoder_call(zr, video, s_std_output, zr->norm); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* toggle JPEG codec sleep to sync PLL */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_sleep(zr, 0); - - /* - * set individual interrupt enables (without GIRQ1) - * but don't global enable until zoran_open() - */ - zr36057_init_vfe(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - - btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts -} - -void zr36057_restart(struct zoran *zr) -{ - btwrite(0, ZR36057_SPGPPCR); - usleep_range(1000, 2000); - btor(ZR36057_SPGPPCR_SOFT_RESET, ZR36057_SPGPPCR); - usleep_range(1000, 2000); - - /* assert P_Reset */ - btwrite(0, ZR36057_JPC); - /* set up GPIO direction - all output */ - btwrite(ZR36057_SPGPPCR_SOFT_RESET | 0, ZR36057_SPGPPCR); - - /* set up GPIO pins and guest bus timing */ - btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); -} - diff --git a/drivers/staging/media/zoran/zoran_device.h b/drivers/staging/media/zoran/zoran_device.h deleted file mode 100644 index 34fd5cc914eb..000000000000 --- a/drivers/staging/media/zoran/zoran_device.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov - */ - -#ifndef __ZORAN_DEVICE_H__ -#define __ZORAN_DEVICE_H__ - -/* general purpose I/O */ -void GPIO(struct zoran *zr, int bit, unsigned int value); - -/* codec (or actually: guest bus) access */ -int post_office_wait(struct zoran *zr); -int post_office_write(struct zoran *zr, unsigned int guest, unsigned int reg, - unsigned int value); -int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg); - -void jpeg_codec_sleep(struct zoran *zr, int sleep); -int jpeg_codec_reset(struct zoran *zr); - -/* zr360x7 access to raw capture */ -void zr36057_overlay(struct zoran *zr, int on); -void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); -void zr36057_set_memgrab(struct zoran *zr, int mode); -int wait_grab_pending(struct zoran *zr); - -/* interrupts */ -void print_interrupts(struct zoran *zr); -void clear_interrupt_counters(struct zoran *zr); -irqreturn_t zoran_irq(int irq, void *dev_id); - -/* JPEG codec access */ -void jpeg_start(struct zoran *zr); -void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode); -void zoran_feed_stat_com(struct zoran *zr); - -/* general */ -void zoran_set_pci_master(struct zoran *zr, int set_master); -void zoran_init_hardware(struct zoran *zr); -void zr36057_restart(struct zoran *zr); - -extern const struct zoran_format zoran_formats[]; - -extern int v4l_bufsize; -extern int jpg_bufsize; -extern int pass_through; - -/* i2c */ -#define decoder_call(zr, o, f, args...) \ - v4l2_subdev_call((zr)->decoder, o, f, ##args) -#define encoder_call(zr, o, f, args...) \ - v4l2_subdev_call((zr)->encoder, o, f, ##args) - -#endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c deleted file mode 100644 index fa672cc8bc67..000000000000 --- a/drivers/staging/media/zoran/zoran_driver.c +++ /dev/null @@ -1,986 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * Copyright (C) 2000 Serguei Miridonov - * - * Changes for BUZ by Wolfgang Scherr - * - * Changes for DC10/DC30 by Laurent Pinchart - * - * Changes for LML33R10 by Maxim Yevtyushkin - * - * Changes for videodev2/v4l2 by Ronald Bultje - * - * Based on - * - * Miro DC10 driver - * Copyright (C) 1999 Wolfgang Scherr - * - * Iomega Buz driver version 1.0 - * Copyright (C) 1999 Rainer Johanni - * - * buz.0.0.3 - * Copyright (C) 1998 Dave Perks - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include "videocodec.h" - -#include -#include - -#include -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - -const struct zoran_format zoran_formats[] = { - { - .name = "15-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB555, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF | - ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "15-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB555X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF, - }, { - .name = "16-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB565, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF | - ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "16-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB565X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF, - }, { - .name = "24-bit RGB", - .fourcc = V4L2_PIX_FMT_BGR24, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 24, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_PACK24, - }, { - .name = "32-bit RGB LE", - .fourcc = V4L2_PIX_FMT_BGR32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "32-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_YUV422, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_YUV422 | ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "Hardware-encoded Motion-JPEG", - .fourcc = V4L2_PIX_FMT_MJPEG, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 0, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_PLAYBACK | - ZORAN_FORMAT_COMPRESSED, - } -}; - -#define NUM_FORMATS ARRAY_SIZE(zoran_formats) - - /* - * small helper function for calculating buffersizes for v4l2 - * we calculate the nearest higher power-of-two, which - * will be the recommended buffersize - */ -static __u32 zoran_v4l2_calc_bufsize(struct zoran_jpg_settings *settings) -{ - __u8 div = settings->ver_dcm * settings->hor_dcm * settings->tmp_dcm; - __u32 num = (1024 * 512) / (div); - __u32 result = 2; - - num--; - while (num) { - num >>= 1; - result <<= 1; - } - - if (result < 8192) - return 8192; - - return result; -} - -/* - * V4L Buffer grabbing - */ -static int zoran_v4l_set_format(struct zoran *zr, int width, int height, - const struct zoran_format *format) -{ - int bpp; - - /* Check size and format of the grab wanted */ - - if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || - height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { - pci_dbg(zr->pci_dev, "%s - wrong frame size (%dx%d)\n", __func__, width, height); - return -EINVAL; - } - - bpp = (format->depth + 7) / 8; - - zr->buffer_size = height * width * bpp; - - /* Check against available buffer size */ - if (height * width * bpp > zr->buffer_size) { - pci_dbg(zr->pci_dev, "%s - video buffer size (%d kB) is too small\n", - __func__, zr->buffer_size >> 10); - return -EINVAL; - } - - /* The video front end needs 4-byte alinged line sizes */ - - if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { - pci_dbg(zr->pci_dev, "%s - wrong frame alignment\n", __func__); - return -EINVAL; - } - - zr->v4l_settings.width = width; - zr->v4l_settings.height = height; - zr->v4l_settings.format = format; - zr->v4l_settings.bytesperline = bpp * zr->v4l_settings.width; - - return 0; -} - -static int zoran_set_norm(struct zoran *zr, v4l2_std_id norm) -{ - if (!(norm & zr->card.norms)) { - pci_dbg(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); - return -EINVAL; - } - - if (norm & V4L2_STD_SECAM) - zr->timing = zr->card.tvn[ZR_NORM_SECAM]; - else if (norm & V4L2_STD_NTSC) - zr->timing = zr->card.tvn[ZR_NORM_NTSC]; - else - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - - decoder_call(zr, video, s_std, norm); - encoder_call(zr, video, s_std_output, norm); - - /* Make sure the changes come into effect */ - zr->norm = norm; - - return 0; -} - -static int zoran_set_input(struct zoran *zr, int input) -{ - if (input == zr->input) - return 0; - - if (input < 0 || input >= zr->card.inputs) { - pci_dbg(zr->pci_dev, "%s - unsupported input %d\n", __func__, input); - return -EINVAL; - } - - zr->input = input; - - decoder_call(zr, video, s_routing, zr->card.input[input].muxsel, 0, 0); - - return 0; -} - -/* - * ioctl routine - */ - -static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) -{ - struct zoran *zr = video_drvdata(file); - - strscpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); - strscpy(cap->driver, "zoran", sizeof(cap->driver)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(zr->pci_dev)); - return 0; -} - -static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) -{ - unsigned int num, i; - - if (fmt->index >= ARRAY_SIZE(zoran_formats)) - return -EINVAL; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - for (num = i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].flags & flag && num++ == fmt->index) { - strscpy(fmt->description, zoran_formats[i].name, - sizeof(fmt->description)); - /* fmt struct pre-zeroed, so adding '\0' not needed */ - fmt->pixelformat = zoran_formats[i].fourcc; - if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - return 0; - } - } - return -EINVAL; -} - -static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran *zr = video_drvdata(file); - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); -} - -static int zoran_g_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - - fmt->fmt.pix.width = zr->jpg_settings.img_width / zr->jpg_settings.hor_dcm; - fmt->fmt.pix.height = zr->jpg_settings.img_height * 2 / - (zr->jpg_settings.ver_dcm * zr->jpg_settings.tmp_dcm); - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - if (zr->jpg_settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - - if (zr->map_mode != ZORAN_MAP_MODE_RAW) - return zoran_g_fmt_vid_out(file, __fh, fmt); - fmt->fmt.pix.width = zr->v4l_settings.width; - fmt->fmt.pix.height = zr->v4l_settings.height; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.pixelformat = zr->v4l_settings.format->fourcc; - fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return 0; -} - -static int zoran_try_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_jpg_settings settings; - int res = 0; - - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - settings = zr->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) - settings.tmp_dcm = 1; - else - settings.tmp_dcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) - settings.ver_dcm = 2; - else - settings.ver_dcm = 1; - if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) - settings.hor_dcm = 4; - else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) - settings.hor_dcm = 2; - else - settings.hor_dcm = 1; - if (settings.tmp_dcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.hor_dcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 1); - if (res) - return res; - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.tmp_dcm * settings.ver_dcm); - if (settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - - fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - int bpp; - int i; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_try_fmt_vid_out(file, __fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) - break; - - if (i == NUM_FORMATS) { - /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ - return -EINVAL; - } - - fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; - fmt->fmt.pix.colorspace = zoran_formats[i].colorspace; - if (BUZ_MAX_HEIGHT < (fmt->fmt.pix.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - - bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); - v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, - bpp == 2 ? 1 : 2, - &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, - 0, 0); - fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * bpp; - fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height; - return 0; -} - -static int zoran_s_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); - struct zoran_jpg_settings settings; - int res = 0; - - pci_dbg(zr->pci_dev, "size=%dx%d, fmt=0x%x (%4.4s)\n", - fmt->fmt.pix.width, fmt->fmt.pix.height, - fmt->fmt.pix.pixelformat, - (char *)&printformat); - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (!fmt->fmt.pix.height || !fmt->fmt.pix.width) - return -EINVAL; - - settings = zr->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) - settings.tmp_dcm = 1; - else - settings.tmp_dcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) - settings.ver_dcm = 2; - else - settings.ver_dcm = 1; - if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) - settings.hor_dcm = 4; - else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) - settings.hor_dcm = 2; - else - settings.hor_dcm = 1; - if (settings.tmp_dcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.hor_dcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* it's ok, so set them */ - zr->jpg_settings = settings; - - if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - zr->map_mode = ZORAN_MAP_MODE_JPG_REC; - else - zr->map_mode = ZORAN_MAP_MODE_JPG_PLAY; - - zr->buffer_size = zoran_v4l2_calc_bufsize(&zr->jpg_settings); - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.tmp_dcm * settings.ver_dcm); - if (settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_fh *fh = __fh; - int i; - int res = 0; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_s_fmt_vid_out(file, fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) - break; - if (i == NUM_FORMATS) { - pci_dbg(zr->pci_dev, "VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", - fmt->fmt.pix.pixelformat); - /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ - return -EINVAL; - } - - fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) - fmt->fmt.pix.height = BUZ_MIN_HEIGHT; - if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) - fmt->fmt.pix.width = BUZ_MIN_WIDTH; - - zr->map_mode = ZORAN_MAP_MODE_RAW; - - res = zoran_v4l_set_format(zr, fmt->fmt.pix.width, fmt->fmt.pix.height, - &zoran_formats[i]); - if (res) - return res; - - /* tell the user the results/missing stuff */ - fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return res; -} - -static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) -{ - struct zoran *zr = video_drvdata(file); - - *std = zr->norm; - return 0; -} - -static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) -{ - struct zoran *zr = video_drvdata(file); - int res = 0; - - if (zr->norm == std) - return 0; - - if (zr->running != ZORAN_MAP_MODE_NONE) - return -EBUSY; - - res = zoran_set_norm(zr, std); - return res; -} - -static int zoran_enum_input(struct file *file, void *__fh, - struct v4l2_input *inp) -{ - struct zoran *zr = video_drvdata(file); - - if (inp->index >= zr->card.inputs) - return -EINVAL; - - strscpy(inp->name, zr->card.input[inp->index].name, sizeof(inp->name)); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - - /* Get status of video decoder */ - decoder_call(zr, video, g_input_status, &inp->status); - return 0; -} - -static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) -{ - struct zoran *zr = video_drvdata(file); - - *input = zr->input; - - return 0; -} - -static int zoran_s_input(struct file *file, void *__fh, unsigned int input) -{ - struct zoran *zr = video_drvdata(file); - int res; - - if (zr->running != ZORAN_MAP_MODE_NONE) - return -EBUSY; - - res = zoran_set_input(zr, input); - return res; -} - -/* cropping (sub-frame capture) */ -static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran *zr = video_drvdata(file); - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pci_dbg(zr->pci_dev, "%s invalid selection type combination\n", __func__); - return -EINVAL; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r.top = zr->jpg_settings.img_y; - sel->r.left = zr->jpg_settings.img_x; - sel->r.width = zr->jpg_settings.img_width; - sel->r.height = zr->jpg_settings.img_height; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = BUZ_MIN_WIDTH; - sel->r.height = BUZ_MIN_HEIGHT; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = BUZ_MAX_WIDTH; - sel->r.height = BUZ_MAX_HEIGHT; - break; - default: - return -EINVAL; - } - return 0; -} - -static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_jpg_settings settings; - int res; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!sel->r.width || !sel->r.height) - return -EINVAL; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (zr->map_mode == ZORAN_MAP_MODE_RAW) { - pci_dbg(zr->pci_dev, "VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n"); - return -EINVAL; - } - - settings = zr->jpg_settings; - - /* move into a form that we understand */ - settings.img_x = sel->r.left; - settings.img_y = sel->r.top; - settings.img_width = sel->r.width; - settings.img_height = sel->r.height; - - /* check validity */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* accept */ - zr->jpg_settings = settings; - return res; -} - -/* - * Output is disabled temporarily - * Zoran is picky about jpeg data it accepts. At least it seems to unsupport COM and APPn. - * So until a way to filter data will be done, disable output. - */ -static const struct v4l2_ioctl_ops zoran_ioctl_ops = { - .vidioc_querycap = zoran_querycap, - .vidioc_s_selection = zoran_s_selection, - .vidioc_g_selection = zoran_g_selection, - .vidioc_enum_input = zoran_enum_input, - .vidioc_g_input = zoran_g_input, - .vidioc_s_input = zoran_s_input, - .vidioc_g_std = zoran_g_std, - .vidioc_s_std = zoran_s_std, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations zoran_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -const struct video_device zoran_template = { - .name = ZORAN_NAME, - .fops = &zoran_fops, - .ioctl_ops = &zoran_ioctl_ops, - .release = &zoran_vdev_release, - .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, -}; - -static int zr_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct zoran *zr = vb2_get_drv_priv(vq); - unsigned int size = zr->buffer_size; - - pci_dbg(zr->pci_dev, "%s nbuf=%u nplanes=%u", __func__, *nbuffers, *nplanes); - - zr->buf_in_reserve = 0; - - if (*nbuffers < vq->min_buffers_needed) - *nbuffers = vq->min_buffers_needed; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - else - return 0; - } - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static void zr_vb2_queue(struct vb2_buffer *vb) -{ - struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); - struct zr_buffer *buf = vb2_to_zr_buffer(vb); - unsigned long flags; - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - list_add_tail(&buf->queue, &zr->queued_bufs); - zr->buf_in_reserve++; - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->running == ZORAN_MAP_MODE_JPG_REC) - zoran_feed_stat_com(zr); - zr->queued++; -} - -static int zr_vb2_prepare(struct vb2_buffer *vb) -{ - struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); - - if (vb2_plane_size(vb, 0) < zr->buffer_size) - return -EINVAL; - zr->prepared++; - - return 0; -} - -int zr_set_buf(struct zoran *zr) -{ - struct zr_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr; - unsigned long flags; - u32 reg; - - if (zr->running == ZORAN_MAP_MODE_NONE) - return 0; - - if (zr->inuse[0]) { - buf = zr->inuse[0]; - buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); - buf->vbuf.sequence = zr->vbseq++; - vbuf = &buf->vbuf; - - buf->vbuf.field = V4L2_FIELD_INTERLACED; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - buf->vbuf.field = V4L2_FIELD_INTERLACED; - else - buf->vbuf.field = V4L2_FIELD_TOP; - vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, zr->buffer_size); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); - zr->inuse[0] = NULL; - } - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - if (list_empty(&zr->queued_bufs)) { - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - vb2_queue_error(zr->video_dev->queue); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return -EINVAL; - } - buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); - if (!buf) { - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - vb2_queue_error(zr->video_dev->queue); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return -EINVAL; - } - list_del(&buf->queue); - zr->buf_in_reserve--; - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - - vbuf = &buf->vbuf; - vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; - phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - - if (!phys_addr) - return -EINVAL; - - zr->inuse[0] = buf; - - reg = phys_addr; - btwrite(reg, ZR36057_VDTR); - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - btwrite(reg, ZR36057_VDBR); - - reg = 0; - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - reg = (reg << ZR36057_VSSFGR_DISP_STRIDE); - reg |= ZR36057_VSSFGR_VID_OVF; - reg |= ZR36057_VSSFGR_SNAP_SHOT; - reg |= ZR36057_VSSFGR_FRAME_GRAB; - btwrite(reg, ZR36057_VSSFGR); - - btor(ZR36057_VDCR_VID_EN, ZR36057_VDCR); - return 0; -} - -static int zr_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct zoran *zr = vq->drv_priv; - int j; - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = cpu_to_le32(1); - zr->inuse[j] = NULL; - } - zr->vbseq = 0; - - if (zr->map_mode != ZORAN_MAP_MODE_RAW) { - pci_dbg(zr->pci_dev, "START JPG\n"); - zr36057_restart(zr); - zoran_init_hardware(zr); - if (zr->map_mode == ZORAN_MAP_MODE_JPG_REC) - zr36057_enable_jpg(zr, BUZ_MODE_MOTION_DECOMPRESS); - else - zr36057_enable_jpg(zr, BUZ_MODE_MOTION_COMPRESS); - zoran_feed_stat_com(zr); - jpeg_start(zr); - zr->running = zr->map_mode; - btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - return 0; - } - - pci_dbg(zr->pci_dev, "START RAW\n"); - zr36057_restart(zr); - zoran_init_hardware(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr36057_set_memgrab(zr, 1); - zr->running = zr->map_mode; - btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - return 0; -} - -static void zr_vb2_stop_streaming(struct vb2_queue *vq) -{ - struct zoran *zr = vq->drv_priv; - struct zr_buffer *buf; - unsigned long flags; - int j; - - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - if (zr->map_mode != ZORAN_MAP_MODE_RAW) - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr36057_set_memgrab(zr, 0); - zr->running = ZORAN_MAP_MODE_NONE; - - zoran_set_pci_master(zr, 0); - - if (!pass_through) { /* Switch to color bar */ - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = cpu_to_le32(1); - if (!zr->inuse[j]) - continue; - buf = zr->inuse[j]; - pci_dbg(zr->pci_dev, "%s clean buf %d\n", __func__, j); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); - zr->inuse[j] = NULL; - } - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while (!list_empty(&zr->queued_bufs)) { - buf = list_entry(zr->queued_bufs.next, struct zr_buffer, queue); - list_del(&buf->queue); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); - zr->buf_in_reserve--; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->buf_in_reserve) - pci_dbg(zr->pci_dev, "Buffer remaining %d\n", zr->buf_in_reserve); - zr->map_mode = ZORAN_MAP_MODE_RAW; -} - -static const struct vb2_ops zr_video_qops = { - .queue_setup = zr_vb2_queue_setup, - .buf_queue = zr_vb2_queue, - .buf_prepare = zr_vb2_prepare, - .start_streaming = zr_vb2_start_streaming, - .stop_streaming = zr_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) -{ - int err; - - spin_lock_init(&zr->queued_bufs_lock); - INIT_LIST_HEAD(&zr->queued_bufs); - - vq->dev = &zr->pci_dev->dev; - vq->type = dir; - - vq->io_modes = VB2_DMABUF | VB2_MMAP; - vq->drv_priv = zr; - vq->buf_struct_size = sizeof(struct zr_buffer); - vq->ops = &zr_video_qops; - vq->mem_ops = &vb2_dma_contig_memops; - vq->gfp_flags = GFP_DMA32; - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vq->min_buffers_needed = 9; - vq->lock = &zr->lock; - err = vb2_queue_init(vq); - if (err) - return err; - zr->video_dev->queue = vq; - return 0; -} - -void zoran_queue_exit(struct zoran *zr) -{ - vb2_queue_release(zr->video_dev->queue); -} diff --git a/drivers/staging/media/zoran/zr36016.c b/drivers/staging/media/zoran/zr36016.c deleted file mode 100644 index 4b328ad6083f..000000000000 --- a/drivers/staging/media/zoran/zr36016.c +++ /dev/null @@ -1,406 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36016 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr - */ - -#include -#include -#include - -/* headerfile of this module */ -#include "zr36016.h" - -/* codec io API */ -#include "videocodec.h" - -/* - * it doesn't make sense to have more than 20 or so, - * just to prevent some unwanted loops - */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36016_codecs; - -/* - * Local hardware I/O functions: read/write via codec layer - * (registers are located in the master device) - */ - -/* read and write functions */ -static u8 zr36016_read(struct zr36016 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); - - return value; -} - -static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); -} - -/* - * indirect read and write functions - * - * the 016 supports auto-addr-increment, but - * writing it all time cost not much and is safer... - */ -static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - /* just in case something is wrong... */ - if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); - value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; - } else { - zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name); - } - - zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n", - ptr->name, reg, value); - return value; -} - -static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, - value, reg); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->writereg) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); - ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); - } else { - zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name); - } -} - -/* Local helper function: version read */ - -/* version kept in datastructure */ -static u8 zr36016_read_version(struct zr36016 *ptr) -{ - ptr->version = zr36016_read(ptr, 0) >> 4; - return ptr->version; -} - -/* - * Local helper function: basic test of "connectivity", writes/reads - * to/from PAX-Lo register - */ - -static int zr36016_basic_test(struct zr36016 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) { - int i; - - zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); - zrdev_dbg(zr, "%s: registers: ", ptr->name); - for (i = 0; i <= 0x0b; i++) - zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i)); - zrdev_dbg(zr, "\n"); - } - // for testing just write 0, then the default value to a register and read - // it back in both cases - zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { - zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); - return -ENXIO; - } - zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { - zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); - return -ENXIO; - } - // we allow version numbers from 0-3, should be enough, though - zr36016_read_version(ptr); - if (ptr->version & 0x0c) { - zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name, - ptr->version); - return -ENXIO; - } - - return 0; /* looks good! */ -} - -/* Basic datasets & init */ - -static void zr36016_init(struct zr36016 *ptr) -{ - // stop any processing - zr36016_write(ptr, ZR016_GOSTOP, 0); - - // mode setup (yuv422 in and out, compression/expansuon due to mode) - zr36016_write(ptr, ZR016_MODE, - ZR016_YUV422 | ZR016_YUV422_YUV422 | - (ptr->mode == CODEC_DO_COMPRESSION ? - ZR016_COMPRESSION : ZR016_EXPANSION)); - - // misc setup - zr36016_writei(ptr, ZR016I_SETUP1, - (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | - (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); - zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); - - // Window setup - // (no extra offset for now, norm defines offset, default width height) - zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); - zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); - zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); - zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); - zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); - zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); - zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); - zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); - - /* shall we continue now, please? */ - zr36016_write(ptr, ZR016_GOSTOP, 1); -} - -/* - * CODEC API FUNCTIONS - * - * These functions are accessed by the master via the API structure - */ - -/* - * set compression/expansion mode and launches codec - - * this should be the last call from the master before starting processing - */ -static int zr36016_set_mode(struct videocodec *codec, int mode) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36016_init(ptr); - - return 0; -} - -/* set picture size */ -static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", - ptr->name, norm->h_start, norm->v_start, - cap->x, cap->y, cap->width, cap->height, - cap->decimation); - - /* - * if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y for now ... - */ - ptr->width = cap->width; - ptr->height = cap->height; - /* - * (Ronald) This is ugly. zoran_device.c, line 387 - * already mentions what happens if h_start is even - * (blue faces, etc., cr/cb inversed). There's probably - * some good reason why h_start is 0 instead of 1, so I'm - * leaving it to this for now, but really... This can be - * done a lot simpler - */ - ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x; - /* - * Something to note here (I don't understand it), setting - * v_start too high will cause the codec to 'not work'. I - * really don't get it. values of 16 (v_start) already break - * it here. Just '0' seems to work. More testing needed! - */ - ptr->yoff = norm->v_start + cap->y; - /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ - ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; - ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; - - return 0; -} - -/* additional control functions */ -static int zr36016_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", - ptr->name, type, size); - - switch (type) { - case CODEC_G_STATUS: /* get last status - we don't know it ... */ - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != 0) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - default: - return -EINVAL; - } - - return size; -} - -/* - * Exit and unregister function: - * - * Deinitializes Zoran's JPEG processor - */ - -static int zr36016_unset(struct videocodec *codec) -{ - struct zr36016 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36016_codecs--; - return 0; - } - - return -EFAULT; -} - -/* - * Setup and registry function: - * - * Initializes Zoran's JPEG processor - * - * Also sets pixel size, average code size, mode (compr./decompr.) - * (the given size is determined by the processor with the video interface) - */ - -static int zr36016_setup(struct videocodec *codec) -{ - struct zr36016 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs); - - if (zr36016_codecs == MAX_CODECS) { - zrdev_err(zr, "zr36016: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs); - ptr->num = zr36016_codecs++; - ptr->codec = codec; - - //testing - res = zr36016_basic_test(ptr); - if (res < 0) { - zr36016_unset(codec); - return res; - } - //final setup - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 768; - ptr->height = 288; - ptr->xdec = 1; - ptr->ydec = 0; - zr36016_init(ptr); - - zrdev_dbg(zr, "%s: codec v%d attached and running\n", - ptr->name, ptr->version); - - return 0; -} - -static const struct videocodec zr36016_codec = { - .name = "zr36016", - .magic = 0L, /* magic not used */ - .flags = - CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36016, - .setup = zr36016_setup, /* functionality */ - .unset = zr36016_unset, - .set_mode = zr36016_set_mode, - .set_video = zr36016_set_video, - .control = zr36016_control, - /* others are not used */ -}; - -/* HOOK IN DRIVER AS KERNEL MODULE */ - -int zr36016_init_module(void) -{ - zr36016_codecs = 0; - return videocodec_register(&zr36016_codec); -} - -void zr36016_cleanup_module(void) -{ - if (zr36016_codecs) { - pr_debug("zr36016: something's wrong - %d codecs left somehow.\n", - zr36016_codecs); - } - videocodec_unregister(&zr36016_codec); -} diff --git a/drivers/staging/media/zoran/zr36016.h b/drivers/staging/media/zoran/zr36016.h deleted file mode 100644 index 04afba35669d..000000000000 --- a/drivers/staging/media/zoran/zr36016.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36016 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr - */ - -#ifndef ZR36016_H -#define ZR36016_H - -/* data stored for each zoran jpeg codec chip */ -struct zr36016 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // coder status - __u8 version; - // actual coder setup - int mode; - - __u16 xoff; - __u16 yoff; - __u16 width; - __u16 height; - __u16 xdec; - __u16 ydec; -}; - -/* direct register addresses */ -#define ZR016_GOSTOP 0x00 -#define ZR016_MODE 0x01 -#define ZR016_IADDR 0x02 -#define ZR016_IDATA 0x03 - -/* indirect register addresses */ -#define ZR016I_SETUP1 0x00 -#define ZR016I_SETUP2 0x01 -#define ZR016I_NAX_LO 0x02 -#define ZR016I_NAX_HI 0x03 -#define ZR016I_PAX_LO 0x04 -#define ZR016I_PAX_HI 0x05 -#define ZR016I_NAY_LO 0x06 -#define ZR016I_NAY_HI 0x07 -#define ZR016I_PAY_LO 0x08 -#define ZR016I_PAY_HI 0x09 -#define ZR016I_NOL_LO 0x0a -#define ZR016I_NOL_HI 0x0b - -/* possible values for mode register */ -#define ZR016_RGB444_YUV444 0x00 -#define ZR016_RGB444_YUV422 0x01 -#define ZR016_RGB444_YUV411 0x02 -#define ZR016_RGB444_Y400 0x03 -#define ZR016_RGB444_RGB444 0x04 -#define ZR016_YUV444_YUV444 0x08 -#define ZR016_YUV444_YUV422 0x09 -#define ZR016_YUV444_YUV411 0x0a -#define ZR016_YUV444_Y400 0x0b -#define ZR016_YUV444_RGB444 0x0c -#define ZR016_YUV422_YUV422 0x11 -#define ZR016_YUV422_YUV411 0x12 -#define ZR016_YUV422_Y400 0x13 -#define ZR016_YUV411_YUV411 0x16 -#define ZR016_YUV411_Y400 0x17 -#define ZR016_4444_4444 0x19 -#define ZR016_100_100 0x1b - -#define ZR016_RGB444 0x00 -#define ZR016_YUV444 0x20 -#define ZR016_YUV422 0x40 - -#define ZR016_COMPRESSION 0x80 -#define ZR016_EXPANSION 0x80 - -/* possible values for setup 1 register */ -#define ZR016_CKRT 0x80 -#define ZR016_VERT 0x40 -#define ZR016_HORZ 0x20 -#define ZR016_HRFL 0x10 -#define ZR016_DSFL 0x08 -#define ZR016_SBFL 0x04 -#define ZR016_RSTR 0x02 -#define ZR016_CNTI 0x01 - -/* possible values for setup 2 register */ -#define ZR016_SYEN 0x40 -#define ZR016_CCIR 0x04 -#define ZR016_SIGN 0x02 -#define ZR016_YMCS 0x01 - -int zr36016_init_module(void); -void zr36016_cleanup_module(void); -#endif /*fndef ZR36016_H */ diff --git a/drivers/staging/media/zoran/zr36050.c b/drivers/staging/media/zoran/zr36050.c deleted file mode 100644 index b07d7e5c1b4a..000000000000 --- a/drivers/staging/media/zoran/zr36050.c +++ /dev/null @@ -1,817 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36050 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr - */ - -#include -#include -#include -#include - -#include -#include - -/* I/O commands, error codes */ -#include - -/* headerfile of this module */ -#include "zr36050.h" - -/* codec io API */ -#include "videocodec.h" - -/* - * it doesn't make sense to have more than 20 or so, - * just to prevent some unwanted loops - */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36050_codecs; - -/* - * Local hardware I/O functions: - * - * read/write via codec layer (registers are located in the master device) - */ - -/* read and write functions */ -static u8 zr36050_read(struct zr36050 *ptr, u16 reg) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - u8 value = 0; - - /* just in case something is wrong... */ - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); - - return value; -} - -static void zr36050_write(struct zr36050 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", - ptr->name); -} - -/* status is kept in datastructure */ -static u8 zr36050_read_status1(struct zr36050 *ptr) -{ - ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); - - zr36050_read(ptr, 0); - return ptr->status1; -} - -/* scale factor is kept in datastructure */ -static u16 zr36050_read_scalefactor(struct zr36050 *ptr) -{ - ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | - (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36050_read(ptr, 0); - return ptr->scalefact; -} - -/* - * Local helper function: - * - * wait if codec is ready to proceed (end of processing) or time is over - */ - -static void zr36050_wait_end(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - while (!(zr36050_read_status1(ptr) & 0x4)) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - zrdev_err(zr, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status1); - break; - } - } -} - -/* - * Local helper function: basic test of "connectivity", writes/reads - * to/from memory the SOF marker - */ - -static int zr36050_basic_test(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zr36050_write(ptr, ZR050_SOF_IDX, 0x00); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); - if ((zr36050_read(ptr, ZR050_SOF_IDX) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { - zrdev_err(zr, - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - zr36050_write(ptr, ZR050_SOF_IDX, 0xff); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); - if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { - zrdev_err(zr, - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - - zr36050_wait_end(ptr); - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, - "%s: attach failed, jpeg processor failed (end flag)!\n", - ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* Local helper function: simple loop for pushing the init datasets */ - -static int zr36050_pushit(struct zr36050 *ptr, u16 startreg, u16 len, const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) - zr36050_write(ptr, startreg++, data[i++]); - - return i; -} - -/* - * Basic datasets: - * - * jpeg baseline setup data (you find it on lots places in internet, or just - * extract it from any regular .jpg image...) - * - * Could be variable, but until it's not needed it they are just fixed to save - * memory. Otherwise expand zr36050 structure with arrays, push the values to - * it and initialize from there, as e.g. the linux zr36057/60 driver does it. - */ - -static const char zr36050_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36050_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* - * Local helper functions: - * - * calculation and setup of parameter-dependent JPEG baseline segments - * (needed for compression only) - */ - -/* ------------------------------------------------------------------------- */ - -/* - * SOF (start of frame) segment depends on width, height and sampling ratio - * of each color component - */ -static int zr36050_set_sof(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sof_data[34]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | - (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection - } - return zr36050_pushit(ptr, ZR050_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* ------------------------------------------------------------------------- */ - -/* - * SOS (start of scan) segment depends on the used scan components - * of each color component - */ - -static int zr36050_set_sos(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sos_data[16]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36050_pushit(ptr, ZR050_SOS1_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* ------------------------------------------------------------------------- */ - -/* DRI (define restart interval) */ - -static int zr36050_set_dri(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char dri_data[6]; // max. size of register set - - zrdev_dbg(zr, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = ptr->dri >> 8; - dri_data[5] = ptr->dri & 0xff; - return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); -} - -/* - * Setup function: - * - * Setup compression/decompression of Zoran's JPEG processor - * ( see also zoran 36050 manual ) - * - * ... sorry for the spaghetti code ... - */ -static void zr36050_init(struct zr36050 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (ptr->mode == CODEC_DO_COMPRESSION) { - zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); - - /* 050 communicates with 057 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); - - /* encoding table preload for compression */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_TLM); - zr36050_write(ptr, ZR050_OPTIONS, 0); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - /* volume control settings */ - /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ - zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); - zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); - - zr36050_write(ptr, ZR050_AF_HI, 0xff); - zr36050_write(ptr, ZR050_AF_M, 0xff); - zr36050_write(ptr, ZR050_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36050_set_sof(ptr); - sum += zr36050_set_sos(ptr); - sum += zr36050_set_dri(ptr); - - /* - * setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) - */ - zrdev_dbg(zr, "%s: write DQT, DHT, APP\n", ptr->name); - sum += zr36050_pushit(ptr, ZR050_DQT_IDX, - sizeof(zr36050_dqt), zr36050_dqt); - sum += zr36050_pushit(ptr, ZR050_DHT_IDX, - sizeof(zr36050_dht), zr36050_dht); - zr36050_write(ptr, ZR050_APP_IDX, 0xff); - zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); - sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, - ptr->app.data) + 4; - zr36050_write(ptr, ZR050_COM_IDX, 0xff); - zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); - zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); - sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, - ptr->com.data) + 4; - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - zrdev_dbg(zr, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); - - /* compression setup with or without bitrate control */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_PASS2 | - (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); - - /* this headers seem to deliver "valid AVI" jpeg frames */ - zr36050_write(ptr, ZR050_MARKERS_EN, - ZR050_ME_DQT | ZR050_ME_DHT | - ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | - ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); - } else { - zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); - - /* 050 communicates with 055 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, - ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); - - /* encoding table preload */ - zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - zrdev_dbg(zr, "%s: write DHT\n", ptr->name); - zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), - zr36050_dht); - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for expansion */ - zr36050_write(ptr, ZR050_MODE, 0); - zr36050_write(ptr, ZR050_MARKERS_EN, 0); - } - - /* adr on selected, to allow GO from master */ - zr36050_read(ptr, 0); -} - -/* - * CODEC API FUNCTIONS - * - * this functions are accessed by the master via the API structure - */ - -/* - * set compression/expansion mode and launches codec - - * this should be the last call from the master before starting processing - */ -static int zr36050_set_mode(struct videocodec *codec, int mode) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36050_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int size; - - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", - ptr->name, norm->h_start, norm->v_start, - cap->x, cap->y, cap->width, cap->height, - cap->decimation, cap->quality); - /* - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... - */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); - - /* (KM) JPEG quality */ - size = ptr->width * ptr->height; - size *= 16; /* size in bits */ - /* apply quality setting */ - size = size * cap->quality / 200; - - /* Minimum: 1kb */ - if (size < 8192) - size = 8192; - /* Maximum: 7/8 of code buffer */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* - * Set max_block_vol here (previously in zr36050_init, moved - * here for consistency with zr36060 code - */ - zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); - - return 0; -} - -/* additional control functions */ -static int zr36050_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36050_read_status1(ptr); - *ival = ptr->status1; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36050_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* Exit and unregister function: Deinitializes Zoran's JPEG processor */ - -static int zr36050_unset(struct videocodec *codec) -{ - struct zr36050 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, - ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36050_codecs--; - return 0; - } - - return -EFAULT; -} - -/* - * Setup and registry function: - * - * Initializes Zoran's JPEG processor - * - * Also sets pixel size, average code size, mode (compr./decompr.) - * (the given size is determined by the processor with the video interface) - */ - -static int zr36050_setup(struct videocodec *codec) -{ - struct zr36050 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36050: initializing MJPEG subsystem #%d.\n", - zr36050_codecs); - - if (zr36050_codecs == MAX_CODECS) { - zrdev_err(zr, - "zr36050: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", - zr36050_codecs); - ptr->num = zr36050_codecs++; - ptr->codec = codec; - - //testing - res = zr36050_basic_test(ptr); - if (res < 0) { - zr36050_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); - - /* 0 or 1 - fixed file size flag (what is the difference?) */ - ptr->bitrate_ctrl = 0; - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; - ptr->max_block_vol = 240; - ptr->scalefact = 0x100; - ptr->dri = 1; - - /* no app/com marker by default */ - ptr->app.appn = 0; - ptr->app.len = 0; - ptr->com.len = 0; - - zr36050_init(ptr); - - zrdev_info(zr, "%s: codec attached and running\n", - ptr->name); - - return 0; -} - -static const struct videocodec zr36050_codec = { - .name = "zr36050", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36050, - .setup = zr36050_setup, // functionality - .unset = zr36050_unset, - .set_mode = zr36050_set_mode, - .set_video = zr36050_set_video, - .control = zr36050_control, - // others are not used -}; - -/* HOOK IN DRIVER AS KERNEL MODULE */ - -int zr36050_init_module(void) -{ - zr36050_codecs = 0; - return videocodec_register(&zr36050_codec); -} - -void zr36050_cleanup_module(void) -{ - if (zr36050_codecs) { - pr_debug("zr36050: something's wrong - %d codecs left somehow.\n", - zr36050_codecs); - } - videocodec_unregister(&zr36050_codec); -} diff --git a/drivers/staging/media/zoran/zr36050.h b/drivers/staging/media/zoran/zr36050.h deleted file mode 100644 index f9b58f4c77b9..000000000000 --- a/drivers/staging/media/zoran/zr36050.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36050 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr - */ - -#ifndef ZR36050_H -#define ZR36050_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36050 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status1; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* com/app marker */ - struct jpeg_com_marker com; - struct jpeg_app_marker app; -}; - -/* zr36050 register addresses */ -#define ZR050_GO 0x000 -#define ZR050_HARDWARE 0x002 -#define ZR050_MODE 0x003 -#define ZR050_OPTIONS 0x004 -#define ZR050_MBCV 0x005 -#define ZR050_MARKERS_EN 0x006 -#define ZR050_INT_REQ_0 0x007 -#define ZR050_INT_REQ_1 0x008 -#define ZR050_TCV_NET_HI 0x009 -#define ZR050_TCV_NET_MH 0x00a -#define ZR050_TCV_NET_ML 0x00b -#define ZR050_TCV_NET_LO 0x00c -#define ZR050_TCV_DATA_HI 0x00d -#define ZR050_TCV_DATA_MH 0x00e -#define ZR050_TCV_DATA_ML 0x00f -#define ZR050_TCV_DATA_LO 0x010 -#define ZR050_SF_HI 0x011 -#define ZR050_SF_LO 0x012 -#define ZR050_AF_HI 0x013 -#define ZR050_AF_M 0x014 -#define ZR050_AF_LO 0x015 -#define ZR050_ACV_HI 0x016 -#define ZR050_ACV_MH 0x017 -#define ZR050_ACV_ML 0x018 -#define ZR050_ACV_LO 0x019 -#define ZR050_ACT_HI 0x01a -#define ZR050_ACT_MH 0x01b -#define ZR050_ACT_ML 0x01c -#define ZR050_ACT_LO 0x01d -#define ZR050_ACV_TURN_HI 0x01e -#define ZR050_ACV_TURN_MH 0x01f -#define ZR050_ACV_TURN_ML 0x020 -#define ZR050_ACV_TURN_LO 0x021 -#define ZR050_STATUS_0 0x02e -#define ZR050_STATUS_1 0x02f - -#define ZR050_SOF_IDX 0x040 -#define ZR050_SOS1_IDX 0x07a -#define ZR050_SOS2_IDX 0x08a -#define ZR050_SOS3_IDX 0x09a -#define ZR050_SOS4_IDX 0x0aa -#define ZR050_DRI_IDX 0x0c0 -#define ZR050_DNL_IDX 0x0c6 -#define ZR050_DQT_IDX 0x0cc -#define ZR050_DHT_IDX 0x1d4 -#define ZR050_APP_IDX 0x380 -#define ZR050_COM_IDX 0x3c0 - -/* zr36050 hardware register bits */ - -#define ZR050_HW_BSWD 0x80 -#define ZR050_HW_MSTR 0x40 -#define ZR050_HW_DMA 0x20 -#define ZR050_HW_CFIS_1_CLK 0x00 -#define ZR050_HW_CFIS_2_CLK 0x04 -#define ZR050_HW_CFIS_3_CLK 0x08 -#define ZR050_HW_CFIS_4_CLK 0x0C -#define ZR050_HW_CFIS_5_CLK 0x10 -#define ZR050_HW_CFIS_6_CLK 0x14 -#define ZR050_HW_CFIS_7_CLK 0x18 -#define ZR050_HW_CFIS_8_CLK 0x1C -#define ZR050_HW_BELE 0x01 - -/* zr36050 mode register bits */ - -#define ZR050_MO_COMP 0x80 -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 -#define ZR050_MO_BRC 0x04 - -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 - -/* zr36050 option register bits */ - -#define ZR050_OP_NSCN_1 0x00 -#define ZR050_OP_NSCN_2 0x20 -#define ZR050_OP_NSCN_3 0x40 -#define ZR050_OP_NSCN_4 0x60 -#define ZR050_OP_NSCN_5 0x80 -#define ZR050_OP_NSCN_6 0xA0 -#define ZR050_OP_NSCN_7 0xC0 -#define ZR050_OP_NSCN_8 0xE0 -#define ZR050_OP_OVF 0x10 - -/* zr36050 markers-enable register bits */ - -#define ZR050_ME_APP 0x80 -#define ZR050_ME_COM 0x40 -#define ZR050_ME_DRI 0x20 -#define ZR050_ME_DQT 0x10 -#define ZR050_ME_DHT 0x08 -#define ZR050_ME_DNL 0x04 -#define ZR050_ME_DQTI 0x02 -#define ZR050_ME_DHTI 0x01 - -/* zr36050 status0/1 register bit masks */ - -#define ZR050_ST_RST_MASK 0x20 -#define ZR050_ST_SOF_MASK 0x02 -#define ZR050_ST_SOS_MASK 0x02 -#define ZR050_ST_DATRDY_MASK 0x80 -#define ZR050_ST_MRKDET_MASK 0x40 -#define ZR050_ST_RFM_MASK 0x10 -#define ZR050_ST_RFD_MASK 0x08 -#define ZR050_ST_END_MASK 0x04 -#define ZR050_ST_TCVOVF_MASK 0x02 -#define ZR050_ST_DATOVF_MASK 0x01 - -/* pixel component idx */ - -#define ZR050_Y_COMPONENT 0 -#define ZR050_U_COMPONENT 1 -#define ZR050_V_COMPONENT 2 - -int zr36050_init_module(void); -void zr36050_cleanup_module(void); -#endif /*fndef ZR36050_H */ diff --git a/drivers/staging/media/zoran/zr36057.h b/drivers/staging/media/zoran/zr36057.h deleted file mode 100644 index 45d8afc62b37..000000000000 --- a/drivers/staging/media/zoran/zr36057.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * zr36057.h - zr36057 register offsets - * - * Copyright (C) 1998 Dave Perks - */ - -#ifndef _ZR36057_H_ -#define _ZR36057_H_ - -/* Zoran ZR36057 registers */ - -#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ -#define ZR36057_VFEHCR_HS_POL BIT(30) -#define ZR36057_VFEHCR_H_START 10 -#define ZR36057_VFEHCR_H_END 0 -#define ZR36057_VFEHCR_HMASK 0x3ff - -#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ -#define ZR36057_VFEVCR_VS_POL BIT(30) -#define ZR36057_VFEVCR_V_START 10 -#define ZR36057_VFEVCR_V_END 0 -#define ZR36057_VFEVCR_VMASK 0x3ff - -#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ -#define ZR36057_VFESPFR_EXT_FL BIT(26) -#define ZR36057_VFESPFR_TOP_FIELD BIT(25) -#define ZR36057_VFESPFR_VCLK_POL BIT(24) -#define ZR36057_VFESPFR_H_FILTER 21 -#define ZR36057_VFESPFR_HOR_DCM 14 -#define ZR36057_VFESPFR_VER_DCM 8 -#define ZR36057_VFESPFR_DISP_MODE 6 -#define ZR36057_VFESPFR_YUV422 (0 << 3) -#define ZR36057_VFESPFR_RGB888 (1 << 3) -#define ZR36057_VFESPFR_RGB565 (2 << 3) -#define ZR36057_VFESPFR_RGB555 (3 << 3) -#define ZR36057_VFESPFR_ERR_DIF BIT(2) -#define ZR36057_VFESPFR_PACK24 BIT(1) -#define ZR36057_VFESPFR_LITTLE_ENDIAN BIT(0) - -#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ - -#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ - -#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ -#define ZR36057_VSSFGR_DISP_STRIDE 16 -#define ZR36057_VSSFGR_VID_OVF BIT(8) -#define ZR36057_VSSFGR_SNAP_SHOT BIT(1) -#define ZR36057_VSSFGR_FRAME_GRAB BIT(0) - -#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ -#define ZR36057_VDCR_VID_EN BIT(31) -#define ZR36057_VDCR_MIN_PIX 24 -#define ZR36057_VDCR_TRITON BIT(24) -#define ZR36057_VDCR_VID_WIN_HT 12 -#define ZR36057_VDCR_VID_WIN_WID 0 - -#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ - -#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ - -#define ZR36057_OCR 0x024 /* Overlay Control Register */ -#define ZR36057_OCR_OVL_ENABLE BIT(15) -#define ZR36057_OCR_MASK_STRIDE 0 - -#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ -#define ZR36057_SPGPPCR_SOFT_RESET BIT(24) - -#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ - -#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ - -#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ -#define ZR36057_MCTCR_COD_TIME BIT(30) -#define ZR36057_MCTCR_C_EMPTY BIT(29) -#define ZR36057_MCTCR_C_FLUSH BIT(28) -#define ZR36057_MCTCR_COD_GUEST_ID 20 -#define ZR36057_MCTCR_COD_GUEST_REG 16 - -#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ - -#define ZR36057_ISR 0x03c /* Interrupt Status Register */ -#define ZR36057_ISR_GIRQ1 BIT(30) -#define ZR36057_ISR_GIRQ0 BIT(29) -#define ZR36057_ISR_COD_REP_IRQ BIT(28) -#define ZR36057_ISR_JPEG_REP_IRQ BIT(27) - -#define ZR36057_ICR 0x040 /* Interrupt Control Register */ -#define ZR36057_ICR_GIRQ1 BIT(30) -#define ZR36057_ICR_GIRQ0 BIT(29) -#define ZR36057_ICR_COD_REP_IRQ BIT(28) -#define ZR36057_ICR_JPEG_REP_IRQ BIT(27) -#define ZR36057_ICR_INT_PIN_EN BIT(24) - -#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ -#define ZR36057_I2CBR_SDA BIT(1) -#define ZR36057_I2CBR_SCL BIT(0) - -#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ -#define ZR36057_JMC_JPG BIT(31) -#define ZR36057_JMC_JPG_EXP_MODE (0 << 29) -#define ZR36057_JMC_JPG_CMP_MODE BIT(29) -#define ZR36057_JMC_MJPG_EXP_MODE (2 << 29) -#define ZR36057_JMC_MJPG_CMP_MODE (3 << 29) -#define ZR36057_JMC_RTBUSY_FB BIT(6) -#define ZR36057_JMC_GO_EN BIT(5) -#define ZR36057_JMC_SYNC_MSTR BIT(4) -#define ZR36057_JMC_FLD_PER_BUFF BIT(3) -#define ZR36057_JMC_VFIFO_FB BIT(2) -#define ZR36057_JMC_CFIFO_FB BIT(1) -#define ZR36057_JMC_STLL_LIT_ENDIAN BIT(0) - -#define ZR36057_JPC 0x104 /* JPEG Process Control */ -#define ZR36057_JPC_P_RESET BIT(7) -#define ZR36057_JPC_COD_TRNS_EN BIT(5) -#define ZR36057_JPC_ACTIVE BIT(0) - -#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ -#define ZR36057_VSP_VSYNC_SIZE 16 -#define ZR36057_VSP_FRM_TOT 0 - -#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ -#define ZR36057_HSP_HSYNC_START 16 -#define ZR36057_HSP_LINE_TOT 0 - -#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ -#define ZR36057_FHAP_NAX 16 -#define ZR36057_FHAP_PAX 0 - -#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ -#define ZR36057_FVAP_NAY 16 -#define ZR36057_FVAP_PAY 0 - -#define ZR36057_FPP 0x118 /* Field Process Parameters */ -#define ZR36057_FPP_ODD_EVEN BIT(0) - -#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ - -#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ - -#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ -#define ZR36057_JCGI_JPE_GUEST_ID 4 -#define ZR36057_JCGI_JPE_GUEST_REG 0 - -#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ - -#define ZR36057_POR 0x200 /* Post Office Register */ -#define ZR36057_POR_PO_PEN BIT(25) -#define ZR36057_POR_PO_TIME BIT(24) -#define ZR36057_POR_PO_DIR BIT(23) - -#define ZR36057_STR 0x300 /* "Still" Transfer Register */ - -#endif diff --git a/drivers/staging/media/zoran/zr36060.c b/drivers/staging/media/zoran/zr36060.c deleted file mode 100644 index 75fd167603dc..000000000000 --- a/drivers/staging/media/zoran/zr36060.c +++ /dev/null @@ -1,870 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36060 basic configuration functions - * - * Copyright (C) 2002 Laurent Pinchart - */ - -#include -#include -#include -#include - -#include -#include - -/* I/O commands, error codes */ -#include - -/* headerfile of this module */ -#include "zr36060.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36060_codecs; - -static bool low_bitrate; -module_param(low_bitrate, bool, 0); -MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); - -/* ========================================================================= - * Local hardware I/O functions: - * read/write via codec layer (registers are located in the master device) - * ========================================================================= - */ - -static u8 zr36060_read(struct zr36060 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - // just in case something is wrong... - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xff; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - return value; -} - -static void zr36060_write(struct zr36060 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "0x%02x @0x%04x\n", value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); -} - -/* ========================================================================= - * Local helper function: - * status read - * ========================================================================= - */ - -/* status is kept in datastructure */ -static u8 zr36060_read_status(struct zr36060 *ptr) -{ - ptr->status = zr36060_read(ptr, ZR060_CFSR); - - zr36060_read(ptr, 0); - return ptr->status; -} - -/* scale factor is kept in datastructure */ -static u16 zr36060_read_scalefactor(struct zr36060 *ptr) -{ - ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | - (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36060_read(ptr, 0); - return ptr->scalefact; -} - -/* wait if codec is ready to proceed (end of processing) or time is over */ -static void zr36060_wait_end(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - while (zr36060_read_status(ptr) & ZR060_CFSR_BUSY) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - zrdev_dbg(zr, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status); - break; - } - } -} - -/* Basic test of "connectivity", writes/reads to/from memory the SOF marker */ -static int zr36060_basic_test(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && - (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { - zrdev_err(zr, "%s: attach failed, can't connect to jpeg processor!\n", ptr->name); - return -ENXIO; - } - - zr36060_wait_end(ptr); - if (ptr->status & ZR060_CFSR_BUSY) { - zrdev_err(zr, "%s: attach failed, jpeg processor failed (end flag)!\n", ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* simple loop for pushing the init datasets */ -static int zr36060_pushit(struct zr36060 *ptr, u16 startreg, u16 len, const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) - zr36060_write(ptr, startreg++, data[i++]); - - return i; -} - -/* ========================================================================= - * Basic datasets: - * jpeg baseline setup data (you find it on lots places in internet, or just - * extract it from any regular .jpg image...) - * - * Could be variable, but until it's not needed it they are just fixed to save - * memory. Otherwise expand zr36060 structure with arrays, push the values to - * it and initialize from there, as e.g. the linux zr36057/60 driver does it. - * ========================================================================= - */ -static const char zr36060_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36060_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* - * SOF (start of frame) segment depends on width, height and sampling ratio - * of each color component - */ -static int zr36060_set_sof(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sof_data[34]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | - (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection - } - return zr36060_pushit(ptr, ZR060_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* SOS (start of scan) segment depends on the used scan components of each color component */ -static int zr36060_set_sos(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sos_data[16]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | - zr36060_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36060_pushit(ptr, ZR060_SOS_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* DRI (define restart interval) */ -static int zr36060_set_dri(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char dri_data[6]; // max. size of register set - - zrdev_dbg(zr, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = (ptr->dri) >> 8; - dri_data[5] = (ptr->dri) & 0xff; - return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); -} - -/* Setup compression/decompression of Zoran's JPEG processor ( see also zoran 36060 manual ) - * ... sorry for the spaghetti code ... - */ -static void zr36060_init(struct zr36060 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (ptr->mode == CODEC_DO_COMPRESSION) { - zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); - - /* Compression with or without variable scale factor */ - /*FIXME: What about ptr->bitrate_ctrl? */ - zr36060_write(ptr, ZR060_CMR, ZR060_CMR_COMP | ZR060_CMR_PASS2 | ZR060_CMR_BRB); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* volume control settings */ - zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); - zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); - - zr36060_write(ptr, ZR060_AF_HI, 0xff); - zr36060_write(ptr, ZR060_AF_M, 0xff); - zr36060_write(ptr, ZR060_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36060_set_sof(ptr); - sum += zr36060_set_sos(ptr); - sum += zr36060_set_dri(ptr); - -/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ - sum += zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), zr36060_dqt); - sum += zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); - zr36060_write(ptr, ZR060_APP_IDX, 0xff); - zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); - sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, ptr->app.data) + 4; - zr36060_write(ptr, ZR060_COM_IDX, 0xff); - zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); - zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); - sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, ptr->com.data) + 4; - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - zrdev_dbg(zr, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); - - /* JPEG markers to be included in the compressed stream */ - zr36060_write(ptr, ZR060_MER, - ZR060_MER_DQT | ZR060_MER_DHT | - ((ptr->com.len > 0) ? ZR060_MER_COM : 0) | - ((ptr->app.len > 0) ? ZR060_MER_APP : 0)); - - /* Setup the Video Frontend */ - /* Limit pixel range to 16..235 as per CCIR-601 */ - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); - - } else { - zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); - - /* Decompression */ - zr36060_write(ptr, ZR060_CMR, 0); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* setup misc. data for expansion */ - zr36060_write(ptr, ZR060_MER, 0); - -/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ - zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); - - /* Setup the Video Frontend */ - //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FI_EXT); - //this doesn't seem right and doesn't work... - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); - } - - /* Load the tables */ - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST | ZR060_LOAD_LOAD); - zr36060_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status); - - if (ptr->status & ZR060_CFSR_BUSY) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } -} - -/* ========================================================================= - * CODEC API FUNCTIONS - * this functions are accessed by the master via the API structure - * ========================================================================= - */ - -/* set compressiion/expansion mode and launches codec - - * this should be the last call from the master before starting processing - */ -static int zr36060_set_mode(struct videocodec *codec, int mode) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if (mode != CODEC_DO_EXPANSION && mode != CODEC_DO_COMPRESSION) - return -EINVAL; - - ptr->mode = mode; - zr36060_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int zr36060_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - u32 reg; - int size; - - zrdev_dbg(zr, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, - cap->x, cap->y, cap->width, cap->height, cap->decimation); - - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... - */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / (cap->decimation >> 8); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* Note that VSPol/HSPol bits in zr36060 have the opposite - * meaning of their zr360x7 counterparts with the same names - * N.b. for VSPol this is only true if FIVEdge = 0 (default, - * left unchanged here - in accordance with datasheet). - */ - reg = (!pol->vsync_pol ? ZR060_VPR_VS_POL : 0) - | (!pol->hsync_pol ? ZR060_VPR_HS_POL : 0) - | (pol->field_pol ? ZR060_VPR_FI_POL : 0) - | (pol->blank_pol ? ZR060_VPR_BL_POL : 0) - | (pol->subimg_pol ? ZR060_VPR_S_IMG_POL : 0) - | (pol->poe_pol ? ZR060_VPR_POE_POL : 0) - | (pol->pvalid_pol ? ZR060_VPR_P_VAL_POL : 0) - | (pol->vclk_pol ? ZR060_VPR_VCLK_POL : 0); - zr36060_write(ptr, ZR060_VPR, reg); - - reg = 0; - switch (cap->decimation & 0xff) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_H_SCALE2; - break; - - case 4: - reg |= ZR060_SR_H_SCALE4; - break; - } - - switch (cap->decimation >> 8) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_V_SCALE; - break; - } - zr36060_write(ptr, ZR060_SR, reg); - - zr36060_write(ptr, ZR060_BCR_Y, 0x00); - zr36060_write(ptr, ZR060_BCR_U, 0x80); - zr36060_write(ptr, ZR060_BCR_V, 0x80); - - /* sync generator */ - - reg = norm->ht - 1; /* Vtotal */ - zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); - - reg = norm->wt - 1; /* Htotal */ - zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); - - reg = 6 - 1; /* VsyncSize */ - zr36060_write(ptr, ZR060_SGR_VSYNC, reg); - - reg = 68; - zr36060_write(ptr, ZR060_SGR_HSYNC, reg); - - reg = norm->v_start - 1; /* BVstart */ - zr36060_write(ptr, ZR060_SGR_BVSTART, reg); - - reg += norm->ha / 2; /* BVend */ - zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); - - reg = norm->h_start - 1; /* BHstart */ - zr36060_write(ptr, ZR060_SGR_BHSTART, reg); - - reg += norm->wa; /* BHend */ - zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); - - /* active area */ - reg = cap->y + norm->v_start; /* Vstart */ - zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); - - reg += cap->height; /* Vend */ - zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); - - reg = cap->x + norm->h_start; /* Hstart */ - zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); - - reg += cap->width; /* Hend */ - zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); - - /* subimage area */ - reg = norm->v_start - 4; /* SVstart */ - zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); - - reg += norm->ha / 2 + 8; /* SVend */ - zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); - - reg = norm->h_start /*+ 64 */ - 4; /* SHstart */ - zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); - - reg += norm->wa + 8; /* SHend */ - zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); - - size = ptr->width * ptr->height; - /* Target compressed field size in bits: */ - size = size * 16; /* uncompressed size in bits */ - /* (Ronald) by default, quality = 100 is a compression - * ratio 1:2. Setting low_bitrate (insmod option) sets - * it to 1:4 (instead of 1:2, zr36060 max) as limit because the - * buz can't handle more at decimation=1... Use low_bitrate if - * you have a Buz, unless you know what you're doing - */ - size = size * cap->quality / (low_bitrate ? 400 : 200); - /* Lower limit (arbitrary, 1 KB) */ - if (size < 8192) - size = 8192; - /* Upper limit: 7/8 of the code buffers */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* the MBCVR is the *maximum* block volume, according to the - * JPEG ISO specs, this shouldn't be used, since that allows - * for the best encoding quality. So set it to it's max value - */ - reg = ptr->max_block_vol; - zr36060_write(ptr, ZR060_MBCVR, reg); - - return 0; -} - -/* additional control functions */ -static int zr36060_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36060_read_status(ptr); - *ival = ptr->status; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36060_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - * Exit and unregister function: - * Deinitializes Zoran's JPEG processor - * ========================================================================= - */ -static int zr36060_unset(struct videocodec *codec) -{ - struct zr36060 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36060_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - * Setup and registry function: - * Initializes Zoran's JPEG processor - * Also sets pixel size, average code size, mode (compr./decompr.) - * (the given size is determined by the processor with the video interface) - * ========================================================================= - */ -static int zr36060_setup(struct videocodec *codec) -{ - struct zr36060 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36060: initializing MJPEG subsystem #%d.\n", - zr36060_codecs); - - if (zr36060_codecs == MAX_CODECS) { - zrdev_err(zr, "zr36060: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", zr36060_codecs); - ptr->num = zr36060_codecs++; - ptr->codec = codec; - - //testing - res = zr36060_basic_test(ptr); - if (res < 0) { - zr36060_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); - - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag (what is the difference?) */ - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; /* CHECKME */ - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ - ptr->scalefact = 0x100; - ptr->dri = 1; /* CHECKME, was 8 is 1 */ - - /* by default, no COM or APP markers - app should set those */ - ptr->com.len = 0; - ptr->app.appn = 0; - ptr->app.len = 0; - - zr36060_init(ptr); - - zrdev_info(zr, "%s: codec attached and running\n", ptr->name); - - return 0; -} - -static const struct videocodec zr36060_codec = { - .name = "zr36060", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER | CODEC_FLAG_VFE, - .type = CODEC_TYPE_ZR36060, - .setup = zr36060_setup, // functionality - .unset = zr36060_unset, - .set_mode = zr36060_set_mode, - .set_video = zr36060_set_video, - .control = zr36060_control, - // others are not used -}; - -int zr36060_init_module(void) -{ - zr36060_codecs = 0; - return videocodec_register(&zr36060_codec); -} - -void zr36060_cleanup_module(void) -{ - if (zr36060_codecs) { - pr_debug("zr36060: something's wrong - %d codecs left somehow.\n", - zr36060_codecs); - } - - /* however, we can't just stay alive */ - videocodec_unregister(&zr36060_codec); -} diff --git a/drivers/staging/media/zoran/zr36060.h b/drivers/staging/media/zoran/zr36060.h deleted file mode 100644 index 75c88677a4bd..000000000000 --- a/drivers/staging/media/zoran/zr36060.h +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36060 basic configuration functions - header file - * - * Copyright (C) 2002 Laurent Pinchart - */ - -#ifndef ZR36060_H -#define ZR36060_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36060 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* app/com marker data */ - struct jpeg_app_marker app; - struct jpeg_com_marker com; -}; - -/* ZR36060 register addresses */ -#define ZR060_LOAD 0x000 -#define ZR060_CFSR 0x001 -#define ZR060_CIR 0x002 -#define ZR060_CMR 0x003 -#define ZR060_MBZ 0x004 -#define ZR060_MBCVR 0x005 -#define ZR060_MER 0x006 -#define ZR060_IMR 0x007 -#define ZR060_ISR 0x008 -#define ZR060_TCV_NET_HI 0x009 -#define ZR060_TCV_NET_MH 0x00a -#define ZR060_TCV_NET_ML 0x00b -#define ZR060_TCV_NET_LO 0x00c -#define ZR060_TCV_DATA_HI 0x00d -#define ZR060_TCV_DATA_MH 0x00e -#define ZR060_TCV_DATA_ML 0x00f -#define ZR060_TCV_DATA_LO 0x010 -#define ZR060_SF_HI 0x011 -#define ZR060_SF_LO 0x012 -#define ZR060_AF_HI 0x013 -#define ZR060_AF_M 0x014 -#define ZR060_AF_LO 0x015 -#define ZR060_ACV_HI 0x016 -#define ZR060_ACV_MH 0x017 -#define ZR060_ACV_ML 0x018 -#define ZR060_ACV_LO 0x019 -#define ZR060_ACT_HI 0x01a -#define ZR060_ACT_MH 0x01b -#define ZR060_ACT_ML 0x01c -#define ZR060_ACT_LO 0x01d -#define ZR060_ACV_TURN_HI 0x01e -#define ZR060_ACV_TURN_MH 0x01f -#define ZR060_ACV_TURN_ML 0x020 -#define ZR060_ACV_TURN_LO 0x021 -#define ZR060_IDR_DEV 0x022 -#define ZR060_IDR_REV 0x023 -#define ZR060_TCR_HI 0x024 -#define ZR060_TCR_LO 0x025 -#define ZR060_VCR 0x030 -#define ZR060_VPR 0x031 -#define ZR060_SR 0x032 -#define ZR060_BCR_Y 0x033 -#define ZR060_BCR_U 0x034 -#define ZR060_BCR_V 0x035 -#define ZR060_SGR_VTOTAL_HI 0x036 -#define ZR060_SGR_VTOTAL_LO 0x037 -#define ZR060_SGR_HTOTAL_HI 0x038 -#define ZR060_SGR_HTOTAL_LO 0x039 -#define ZR060_SGR_VSYNC 0x03a -#define ZR060_SGR_HSYNC 0x03b -#define ZR060_SGR_BVSTART 0x03c -#define ZR060_SGR_BHSTART 0x03d -#define ZR060_SGR_BVEND_HI 0x03e -#define ZR060_SGR_BVEND_LO 0x03f -#define ZR060_SGR_BHEND_HI 0x040 -#define ZR060_SGR_BHEND_LO 0x041 -#define ZR060_AAR_VSTART_HI 0x042 -#define ZR060_AAR_VSTART_LO 0x043 -#define ZR060_AAR_VEND_HI 0x044 -#define ZR060_AAR_VEND_LO 0x045 -#define ZR060_AAR_HSTART_HI 0x046 -#define ZR060_AAR_HSTART_LO 0x047 -#define ZR060_AAR_HEND_HI 0x048 -#define ZR060_AAR_HEND_LO 0x049 -#define ZR060_SWR_VSTART_HI 0x04a -#define ZR060_SWR_VSTART_LO 0x04b -#define ZR060_SWR_VEND_HI 0x04c -#define ZR060_SWR_VEND_LO 0x04d -#define ZR060_SWR_HSTART_HI 0x04e -#define ZR060_SWR_HSTART_LO 0x04f -#define ZR060_SWR_HEND_HI 0x050 -#define ZR060_SWR_HEND_LO 0x051 - -#define ZR060_SOF_IDX 0x060 -#define ZR060_SOS_IDX 0x07a -#define ZR060_DRI_IDX 0x0c0 -#define ZR060_DQT_IDX 0x0cc -#define ZR060_DHT_IDX 0x1d4 -#define ZR060_APP_IDX 0x380 -#define ZR060_COM_IDX 0x3c0 - -/* ZR36060 LOAD register bits */ - -#define ZR060_LOAD_LOAD BIT(7) -#define ZR060_LOAD_SYNC_RST BIT(0) - -/* ZR36060 Code FIFO Status register bits */ - -#define ZR060_CFSR_BUSY BIT(7) -#define ZR060_CFSR_C_BUSY BIT(2) -#define ZR060_CFSR_CFIFO (3 << 0) - -/* ZR36060 Code Interface register */ - -#define ZR060_CIR_CODE16 BIT(7) -#define ZR060_CIR_ENDIAN BIT(6) -#define ZR060_CIR_CFIS BIT(2) -#define ZR060_CIR_CODE_MSTR BIT(0) - -/* ZR36060 Codec Mode register */ - -#define ZR060_CMR_COMP BIT(7) -#define ZR060_CMR_ATP BIT(6) -#define ZR060_CMR_PASS2 BIT(5) -#define ZR060_CMR_TLM BIT(4) -#define ZR060_CMR_BRB BIT(2) -#define ZR060_CMR_FSF BIT(1) - -/* ZR36060 Markers Enable register */ - -#define ZR060_MER_APP BIT(7) -#define ZR060_MER_COM BIT(6) -#define ZR060_MER_DRI BIT(5) -#define ZR060_MER_DQT BIT(4) -#define ZR060_MER_DHT BIT(3) - -/* ZR36060 Interrupt Mask register */ - -#define ZR060_IMR_EOAV BIT(3) -#define ZR060_IMR_EOI BIT(2) -#define ZR060_IMR_END BIT(1) -#define ZR060_IMR_DATA_ERR BIT(0) - -/* ZR36060 Interrupt Status register */ - -#define ZR060_ISR_PRO_CNT (3 << 6) -#define ZR060_ISR_EOAV BIT(3) -#define ZR060_ISR_EOI BIT(2) -#define ZR060_ISR_END BIT(1) -#define ZR060_ISR_DATA_ERR BIT(0) - -/* ZR36060 Video Control register */ - -#define ZR060_VCR_VIDEO8 BIT(7) -#define ZR060_VCR_RANGE BIT(6) -#define ZR060_VCR_FI_DET BIT(3) -#define ZR060_VCR_FI_VEDGE BIT(2) -#define ZR060_VCR_FI_EXT BIT(1) -#define ZR060_VCR_SYNC_MSTR BIT(0) - -/* ZR36060 Video Polarity register */ - -#define ZR060_VPR_VCLK_POL BIT(7) -#define ZR060_VPR_P_VAL_POL BIT(6) -#define ZR060_VPR_POE_POL BIT(5) -#define ZR060_VPR_S_IMG_POL BIT(4) -#define ZR060_VPR_BL_POL BIT(3) -#define ZR060_VPR_FI_POL BIT(2) -#define ZR060_VPR_HS_POL BIT(1) -#define ZR060_VPR_VS_POL BIT(0) - -/* ZR36060 Scaling register */ - -#define ZR060_SR_V_SCALE BIT(2) -#define ZR060_SR_H_SCALE2 BIT(0) -#define ZR060_SR_H_SCALE4 (2 << 0) - -int zr36060_init_module(void); -void zr36060_cleanup_module(void); -#endif /*fndef ZR36060_H */ -- cgit v1.3-14-g43fede From b02c4a5293521f2f8c4ed1db2012d28802a42696 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:22 +0200 Subject: media: media/pci/ngene/ngene.h: remove #ifdef NGENE_V4L Remove the dead code under NGENE_V4L. Among others, this removes references to the old videobuf framework that isn't actually used by this driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ngene/ngene.h | 78 ----------------------------------------- 1 file changed, 78 deletions(-) diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index 3d296f1998a1..d1d7da84cd9d 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -596,43 +596,6 @@ struct mychip { int capture_source[MIXER_ADDR_LAST + 1][2]; }; -#ifdef NGENE_V4L -struct ngene_overlay { - int tvnorm; - struct v4l2_rect w; - enum v4l2_field field; - struct v4l2_clip *clips; - int nclips; - int setup_ok; -}; - -struct ngene_tvnorm { - int v4l2_id; - char *name; - u16 swidth, sheight; /* scaled standard width, height */ - int tuner_norm; - int soundstd; -}; - -struct ngene_vopen { - struct ngene_channel *ch; - enum v4l2_priority prio; - int width; - int height; - int depth; - struct videobuf_queue vbuf_q; - struct videobuf_queue vbi; - int fourcc; - int picxcount; - int resources; - enum v4l2_buf_type type; - const struct ngene_format *fmt; - - const struct ngene_format *ovfmt; - struct ngene_overlay ov; -}; -#endif - struct ngene_channel { struct device device; struct i2c_adapter i2c_adapter; @@ -709,18 +672,6 @@ struct ngene_channel { int tvnorm_num; int tvnorm; -#ifdef NGENE_V4L - int videousers; - struct v4l2_prio_state prio; - struct ngene_vopen init; - int resources; - struct v4l2_framebuffer fbuf; - struct ngene_buffer *screen; /* overlay */ - struct list_head capture; /* video capture queue */ - spinlock_t s_lock; - struct semaphore reslock; -#endif - int running; int tsin_offset; @@ -863,35 +814,6 @@ struct ngene_info { int (*switch_ctrl)(struct ngene_channel *, int, int); }; -#ifdef NGENE_V4L -struct ngene_format { - char *name; - int fourcc; /* video4linux 2 */ - int btformat; /* BT848_COLOR_FMT_* */ - int format; - int btswap; /* BT848_COLOR_CTL_* */ - int depth; /* bit/pixel */ - int flags; - int hshift, vshift; /* for planar modes */ - int palette; -}; - -#define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 -#define RESOURCE_VBI 4 - -struct ngene_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - /* ngene specific */ - const struct ngene_format *fmt; - int tvnorm; - int btformat; - int btswap; -}; -#endif - /* Provided by ngene-core.c */ int ngene_probe(struct pci_dev *pci_dev, const struct pci_device_id *id); -- cgit v1.3-14-g43fede From 3e947c36af524b382827f584dba6d47317394b41 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:23 +0200 Subject: media: vb2: videobuf -> videobuf2 It is confusing to use the term 'videobuf' or 'video-buf' since that usually refers to the old videobuf version 1 framework. Rename to 'videobuf2' or vb2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 14 +++++++------- drivers/media/common/videobuf2/videobuf2-dvb.c | 4 ++-- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 ++-- include/media/videobuf2-core.h | 16 ++++++++-------- include/media/videobuf2-dvb.h | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b203c1e26353..ab9697f3b5f1 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -398,7 +398,7 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb) } /* - * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type) + * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type) * video buffer memory for all buffers/planes on the queue and initializes the * queue * @@ -417,7 +417,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, VB2_MAX_FRAME - q->num_buffers); for (buffer = 0; buffer < num_buffers; ++buffer) { - /* Allocate videobuf buffer structures */ + /* Allocate vb2 buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { dprintk(q, 1, "memory alloc for buffer struct failed\n"); @@ -599,7 +599,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) } #endif - /* Free videobuf buffers */ + /* Free vb2 buffers */ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; ++buffer) { kfree(q->bufs[buffer]); @@ -1949,7 +1949,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, if (pb) call_void_bufop(q, fill_user_buffer, vb, pb); - /* Remove from videobuf queue */ + /* Remove from vb2 queue */ list_del(&vb->queued_entry); q->queued_count--; @@ -1978,7 +1978,7 @@ EXPORT_SYMBOL_GPL(vb2_core_dqbuf); * __vb2_queue_cancel() - cancel and stop (pause) streaming * * Removes all queued buffers from driver's queue and all buffers queued by - * userspace from videobuf's queue. Returns to state after reqbufs. + * userspace from vb2's queue. Returns to state after reqbufs. */ static void __vb2_queue_cancel(struct vb2_queue *q) { @@ -2016,7 +2016,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) q->uses_qbuf = 0; /* - * Remove all buffers from videobuf's list... + * Remove all buffers from vb2's list... */ INIT_LIST_HEAD(&q->queued_list); /* @@ -2139,7 +2139,7 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) /* * Cancel will pause streaming and remove all buffers from the driver - * and videobuf, effectively returning control over them to userspace. + * and vb2, effectively returning control over them to userspace. * * Note that we do this even if q->streaming == 0: if you prepare or * queue buffers, and then call streamoff without ever having called diff --git a/drivers/media/common/videobuf2/videobuf2-dvb.c b/drivers/media/common/videobuf2/videobuf2-dvb.c index 9d571c9d31e9..8c15bcd07eef 100644 --- a/drivers/media/common/videobuf2/videobuf2-dvb.c +++ b/drivers/media/common/videobuf2/videobuf2-dvb.c @@ -3,8 +3,8 @@ * * some helper function for simple DVB cards which simply DMA the * complete transport stream and let the computer sort everything else - * (i.e. we are using the software demux, ...). Also uses the - * video-buf to manage DMA buffers. + * (i.e. we are using the software demux, ...). Also uses vb2 + * to manage DMA buffers. * * (c) 2004 Gerd Knorr [SUSE Labs] */ diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index f26cb8586bd4..2ecd4483e139 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -268,7 +268,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b /* * Single-planar buffers do not use planes array, * so fill in relevant v4l2_buffer struct fields instead. - * In videobuf we use our internal V4l2_planes struct for + * In vb2 we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. * * If bytesused == 0 for the output buffer, then fall back @@ -652,7 +652,7 @@ EXPORT_SYMBOL_GPL(vb2_find_buffer); /* * vb2_querybuf() - query video buffer information - * @q: videobuf queue + * @q: vb2 queue * @b: buffer struct passed from userspace to vidioc_querybuf handler * in driver * diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 5468b633b9d2..3253bd2f6fee 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -65,7 +65,7 @@ struct vb2_buffer; * DMABUF memory types. * @get_userptr: acquire userspace memory for a hardware operation; used for * USERPTR memory types; vaddr is the address passed to the - * videobuf layer when queuing a video buffer of USERPTR type; + * videobuf2 layer when queuing a video buffer of USERPTR type; * should return an allocator private per-buffer structure * associated with the buffer on success, ERR_PTR() on failure; * the returned private structure will then be passed as @buf_priv @@ -97,7 +97,7 @@ struct vb2_buffer; * associated with the passed private structure or NULL if not * available. * @num_users: return the current number of users of a memory buffer; - * return 1 if the videobuf layer (or actually the driver using + * return 1 if the videobuf2 layer (or actually the driver using * it) is the only user. * @mmap: setup a userspace mapping for a given memory buffer under * the provided virtual memory region. @@ -210,11 +210,11 @@ enum vb2_io_modes { * enum vb2_buffer_state - current video buffer state. * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control. * @VB2_BUF_STATE_IN_REQUEST: buffer is queued in media request. - * @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf. - * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver. + * @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf2. + * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf2, but not in driver. * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used * in a hardware operation. - * @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but + * @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf2, but * not yet dequeued to userspace. * @VB2_BUF_STATE_ERROR: same as above, but the operation on the buffer * has ended with an error, which will be reported @@ -466,7 +466,7 @@ struct vb2_buf_ops { }; /** - * struct vb2_queue - a videobuf queue. + * struct vb2_queue - a videobuf2 queue. * * @type: private buffer type whose content is defined by the vb2-core * caller. For example, for V4L2, it should match @@ -544,7 +544,7 @@ struct vb2_buf_ops { * @mmap_lock: private mutex used when buffers are allocated/freed/mmapped * @memory: current memory type used * @dma_dir: DMA mapping direction. - * @bufs: videobuf buffer structures + * @bufs: videobuf2 buffer structures * @num_buffers: number of allocated/used buffers * @queued_list: list of buffers currently queued from userspace * @queued_count: number of buffers queued and ready for streaming. @@ -683,7 +683,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no); void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no); /** - * vb2_buffer_done() - inform videobuf that an operation on a buffer + * vb2_buffer_done() - inform videobuf2 that an operation on a buffer * is finished. * @vb: pointer to &struct vb2_buffer to be used. * @state: state of the buffer, as defined by &enum vb2_buffer_state. diff --git a/include/media/videobuf2-dvb.h b/include/media/videobuf2-dvb.h index 8605366ec87c..2d577b945637 100644 --- a/include/media/videobuf2-dvb.h +++ b/include/media/videobuf2-dvb.h @@ -24,7 +24,7 @@ struct vb2_dvb { struct dvb_frontend *frontend; struct vb2_queue dvbq; - /* video-buf-dvb state info */ + /* vb2-dvb state info */ struct mutex lock; int nfeeds; -- cgit v1.3-14-g43fede From a13f509b1918924aa722cb6c2caeb3f471235cf6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:24 +0200 Subject: media: media/v4l2-mem2mem.h: rename 'videobuf' to 'vb2' It is confusing to refer to vb2 structures with 'videobuf', since that typically is used to refer to the old videobuf version 1 framework. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- include/media/v4l2-mem2mem.h | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 837e1855f94b..be7fde1ed3ea 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Memory-to-memory device framework for Video for Linux 2 and videobuf. + * Memory-to-memory device framework for Video for Linux 2 and vb2. * - * Helper functions for devices that use videobuf buffers for both their + * Helper functions for devices that use vb2 buffers for both their * source and destination. * * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. @@ -21,7 +21,7 @@ #include #include -MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); +MODULE_DESCRIPTION("Mem to mem device framework for vb2"); MODULE_AUTHOR("Pawel Osciak, "); MODULE_LICENSE("GPL"); diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index fdbd5257e020..bb9de6a899e0 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -486,10 +486,10 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * @vma: pointer to struct &vm_area_struct * * Call from driver's mmap() function. Will handle mmap() for both queues - * seamlessly for videobuffer, which will receive normal per-queue offsets and - * proper videobuf queue pointers. The differentiation is made outside videobuf - * by adding a predefined offset to buffers from one of the queues and - * subtracting it before passing it back to videobuf. Only drivers (and + * seamlessly for the video buffer, which will receive normal per-queue offsets + * and proper vb2 queue pointers. The differentiation is made outside + * vb2 by adding a predefined offset to buffers from one of the queues + * and subtracting it before passing it back to vb2. Only drivers (and * thus applications) receive modified offsets. */ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, @@ -544,7 +544,7 @@ void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); * @m2m_dev: opaque pointer to the internal data to handle M2M context * @drv_priv: driver's instance private data * @queue_init: a callback for queue type-specific initialization function - * to be used for initializing videobuf_queues + * to be used for initializing vb2_queues * * Usually called from driver's ``open()`` function. */ @@ -579,7 +579,7 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx); * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx * @vbuf: pointer to struct &vb2_v4l2_buffer * - * Call from videobuf_queue_ops->ops->buf_queue, videobuf_queue_ops callback. + * Call from vb2_queue_ops->ops->buf_queue, vb2_queue_ops callback. */ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_v4l2_buffer *vbuf); -- cgit v1.3-14-g43fede From 6be954808abd3342c773b3f50dcc8bb291494712 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:25 +0200 Subject: media: platform: ti: avoid using 'videobuf' or 'video-buf' These terms typically refer to the old version 1 videobuf framework. It is confusing to use them for the vb2 framework, so reword these comments. Signed-off-by: Hans Verkuil Reviewed-by: Lad Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/am437x/am437x-vpfe.h | 2 +- drivers/media/platform/ti/davinci/vpif.h | 60 ++++++++++++------------ drivers/media/platform/ti/davinci/vpif_capture.c | 6 +-- drivers/media/platform/ti/davinci/vpif_capture.h | 2 +- drivers/media/platform/ti/davinci/vpif_display.c | 6 +-- drivers/media/platform/ti/davinci/vpif_display.h | 6 +-- drivers/media/platform/ti/omap3isp/ispvideo.c | 2 +- include/media/davinci/vpbe_display.h | 6 +-- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.h b/drivers/media/platform/ti/am437x/am437x-vpfe.h index 05ee37db0273..f8b4e917b91a 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.h +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.h @@ -267,7 +267,7 @@ struct vpfe_device { * is different from the image window */ struct v4l2_rect crop; - /* Buffer queue used in video-buf */ + /* Buffer queue used in vb2 */ struct vb2_queue buffer_queue; /* Queue of filled frames */ struct list_head dma_queue; diff --git a/drivers/media/platform/ti/davinci/vpif.h b/drivers/media/platform/ti/davinci/vpif.h index 651943e3e375..52ecc2562216 100644 --- a/drivers/media/platform/ti/davinci/vpif.h +++ b/drivers/media/platform/ti/davinci/vpif.h @@ -322,10 +322,10 @@ static inline void channel1_intr_enable(int enable) } /* inline function to set buffer addresses in case of Y/C non mux mode */ -static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch0_set_video_buf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); @@ -334,10 +334,10 @@ static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, } /* inline function to set buffer addresses in VPIF registers for video data */ -static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch0_set_video_buf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); @@ -345,10 +345,10 @@ static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA); } -static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch1_set_video_buf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA); @@ -538,10 +538,10 @@ static inline void channel3_clipping_enable(int enable) } /* inline function to set buffer addresses in case of Y/C non mux mode */ -static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch2_set_video_buf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); @@ -550,10 +550,10 @@ static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, } /* inline function to set buffer addresses in VPIF registers for video data */ -static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch2_set_video_buf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); @@ -561,10 +561,10 @@ static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA); } -static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) +static inline void ch3_set_video_buf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA); regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA); @@ -574,18 +574,18 @@ static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, /* inline function to set buffer addresses in VPIF registers for vbi data */ static inline void ch2_set_vbi_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC); regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC); } static inline void ch3_set_vbi_addr(unsigned long top_strt_luma, - unsigned long btm_strt_luma, - unsigned long top_strt_chroma, - unsigned long btm_strt_chroma) + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) { regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC); regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC); diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index b91eec899eb5..580723333fcc 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -632,11 +632,11 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) common = &(ch->common[VPIF_VIDEO_INDEX]); if (VPIF_CHANNEL1_VIDEO == ch->channel_id) - common->set_addr = ch1_set_videobuf_addr; + common->set_addr = ch1_set_video_buf_addr; else if (2 == muxmode) - common->set_addr = ch0_set_videobuf_addr_yc_nmux; + common->set_addr = ch0_set_video_buf_addr_yc_nmux; else - common->set_addr = ch0_set_videobuf_addr; + common->set_addr = ch0_set_video_buf_addr; } /** diff --git a/drivers/media/platform/ti/davinci/vpif_capture.h b/drivers/media/platform/ti/davinci/vpif_capture.h index d5951f61df47..6191056500cf 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.h +++ b/drivers/media/platform/ti/davinci/vpif_capture.h @@ -50,7 +50,7 @@ struct common_obj { struct vpif_cap_buffer *next_frm; /* Used to store pixel format */ struct v4l2_format fmt; - /* Buffer queue used in video-buf */ + /* Buffer queue used in vb2 */ struct vb2_queue buffer_queue; /* Queue of filled frames */ struct list_head dma_queue; diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index 5d524acc995d..b2df81603f62 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -563,12 +563,12 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { - common->set_addr = ch3_set_videobuf_addr; + common->set_addr = ch3_set_video_buf_addr; } else { if (2 == muxmode) - common->set_addr = ch2_set_videobuf_addr_yc_nmux; + common->set_addr = ch2_set_video_buf_addr_yc_nmux; else - common->set_addr = ch2_set_videobuf_addr; + common->set_addr = ch2_set_video_buf_addr; } } diff --git a/drivers/media/platform/ti/davinci/vpif_display.h b/drivers/media/platform/ti/davinci/vpif_display.h index f27474e0fc36..dae20053dd73 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.h +++ b/drivers/media/platform/ti/davinci/vpif_display.h @@ -64,11 +64,11 @@ struct common_obj { struct vpif_disp_buffer *next_frm; /* Pointer pointing to next * vb2_buffer */ struct v4l2_format fmt; /* Used to store the format */ - struct vb2_queue buffer_queue; /* Buffer queue used in - * video-buf */ + struct vb2_queue buffer_queue; /* Buffer queue used in vb2 */ struct list_head dma_queue; /* Queue of filled frames */ - spinlock_t irqlock; /* Used in video-buf */ + spinlock_t irqlock; /* Used for video buffer + * handling */ /* channel specific parameters */ struct mutex lock; /* lock used to access this diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index d7059180e80e..cc9a97d5d505 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -1071,7 +1071,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, * processing might be possible but requires more testing. * * Stream start must be delayed until buffers are available at both the input - * and output. The pipeline must be started in the videobuf queue callback with + * and output. The pipeline must be started in the vb2 queue callback with * the buffers queue spinlock held. The modules subdev set stream operation must * not sleep. */ diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h index 6d2a93740130..d8751ea926a2 100644 --- a/include/media/davinci/vpbe_display.h +++ b/include/media/davinci/vpbe_display.h @@ -69,13 +69,13 @@ struct vpbe_layer { struct vpbe_disp_buffer *cur_frm; /* Pointer pointing to next v4l2_buffer */ struct vpbe_disp_buffer *next_frm; - /* videobuf specific parameters - * Buffer queue used in video-buf + /* vb2 specific parameters + * Buffer queue used in vb2 */ struct vb2_queue buffer_queue; /* Queue of filled frames */ struct list_head dma_queue; - /* Used in video-buf */ + /* Used for video buffer handling */ spinlock_t irqlock; /* V4l2 specific parameters */ /* Identifies video device for this layer */ -- cgit v1.3-14-g43fede From 77423a62db082793f14860c938ebe2b316fe1aae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:26 +0200 Subject: media: staging/media/omap4iss/iss_video.c: videobuf -> vb2 Rename 'videobuf' to 'vb2' in the comment. This avoids confusing terminology since 'videobuf' typically refers to the old vb1 framework. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/omap4iss/iss_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 9512cd3314f2..842509dcfedf 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -843,7 +843,7 @@ iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) * processing might be possible but requires more testing. * * Stream start must be delayed until buffers are available at both the input - * and output. The pipeline must be started in the videobuf queue callback with + * and output. The pipeline must be started in the vb2 queue callback with * the buffers queue spinlock held. The modules subdev set stream operation must * not sleep. */ -- cgit v1.3-14-g43fede From f068a6cec0fbdc479a9776923eb772d76c3948cd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 15:01:27 +0200 Subject: media: avoid use of 'videobuf' The term 'videobuf' typically refers to the old videobuf version 1 framework. Avoid using this word in drivers that are converted to vb2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/drivers/pxa_camera.rst | 2 +- drivers/media/dvb-frontends/rtl2832_sdr.c | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c | 2 +- drivers/media/platform/intel/pxa_camera.c | 8 ++++---- drivers/media/platform/marvell/mcam-core.h | 2 +- drivers/media/platform/renesas/vsp1/vsp1_video.c | 2 +- drivers/media/platform/samsung/exynos4-is/fimc-core.h | 2 +- drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c | 2 +- drivers/media/test-drivers/vim2m.c | 2 +- drivers/media/usb/airspy/airspy.c | 2 +- drivers/media/usb/au0828/au0828-video.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/msi2500/msi2500.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-dvb.c | 2 +- 16 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Documentation/driver-api/media/drivers/pxa_camera.rst b/Documentation/driver-api/media/drivers/pxa_camera.rst index ee1bd96b66dd..46919919baac 100644 --- a/Documentation/driver-api/media/drivers/pxa_camera.rst +++ b/Documentation/driver-api/media/drivers/pxa_camera.rst @@ -19,7 +19,7 @@ Global video workflow a) QCI stopped Initially, the QCI interface is stopped. - When a buffer is queued (pxa_videobuf_ops->buf_queue), the QCI starts. + When a buffer is queued, start_streaming is called and the QCI starts. b) QCI started More buffers can be queued while the QCI is started without halting the diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 6a4f2997d6f5..05f71d169726 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -245,7 +245,7 @@ static void rtl2832_sdr_urb_complete(struct urb *urb) if (unlikely(fbuf == NULL)) { dev->vb_full++; dev_notice_ratelimited(&pdev->dev, - "videobuf is full, %d packets dropped\n", + "video buffer is full, %d packets dropped\n", dev->vb_full); goto skip; } diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 80d20e2a2099..0adf3d80f248 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1020,7 +1020,7 @@ static int solo_g_parm(struct file *file, void *priv, cp->timeperframe.numerator = solo_enc->interval; cp->timeperframe.denominator = solo_enc->solo_dev->fps; cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ + /* XXX: Shouldn't we be able to get/set this from vb2? */ cp->readbuffers = 2; return 0; diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index 35145e3348f0..54270d6b6f50 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -854,7 +854,7 @@ fail: return -ENOMEM; } -static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev, +static void pxa_video_buf_set_actdma(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf) { buf->active_dma = DMA_Y; @@ -973,7 +973,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, * stopped. This means the tailed buffer would never be transferred by DMA. * This function restarts the capture for this corner case, where : * - DADR() == DADDR_STOP - * - a videobuffer is queued on the pcdev->capture list + * - a video buffer is queued on the pcdev->capture list * * Please check the "DMA hot chaining timeslice issue" in * Documentation/driver-api/media/drivers/pxa_camera.rst @@ -1163,7 +1163,7 @@ static void pxa_camera_eof(struct tasklet_struct *t) pcdev->active = list_first_entry(&pcdev->capture, struct pxa_buffer, queue); buf = pcdev->active; - pxa_videobuf_set_actdma(pcdev, buf); + pxa_video_buf_set_actdma(pcdev, buf); pxa_dma_start_channels(pcdev); } @@ -1416,7 +1416,7 @@ static int pxac_vb2_prepare(struct vb2_buffer *vb) * the actual buffer is yours */ buf->inwork = 0; - pxa_videobuf_set_actdma(pcdev, buf); + pxa_video_buf_set_actdma(pcdev, buf); return ret; } diff --git a/drivers/media/platform/marvell/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h index f324d808d737..51e66db45af6 100644 --- a/drivers/media/platform/marvell/mcam-core.h +++ b/drivers/media/platform/marvell/mcam-core.h @@ -32,7 +32,7 @@ #if !defined(MCAM_MODE_VMALLOC) && !defined(MCAM_MODE_DMA_CONTIG) && \ !defined(MCAM_MODE_DMA_SG) -#error One of the videobuf buffer modes must be selected in the config +#error One of the vb2 buffer modes must be selected in the config #endif diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index e8e0ee5f2277..df1606b49d77 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -305,7 +305,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) * @video: the video node * * This function completes the current buffer by filling its sequence number, - * time stamp and payload size, and hands it back to the videobuf core. + * time stamp and payload size, and hands it back to the vb2 core. * * Return the next queued buffer or NULL if the queue is empty. */ diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.h b/drivers/media/platform/samsung/exynos4-is/fimc-core.h index 7a058f3e6298..2b0760add092 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.h @@ -215,7 +215,7 @@ struct fimc_addr { /** * struct fimc_vid_buffer - the driver's video buffer - * @vb: v4l videobuf buffer + * @vb: v4l vb2 buffer * @list: linked list structure for buffer queue * @addr: precalculated DMA address set * @index: buffer index for the output DMA engine diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 761341934925..219fc0235b69 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -323,7 +323,7 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) } ctx->sequence++; /* The MFC returns address of the buffer, now we have to - * check which videobuf does it correspond to */ + * check which vb2_buffer does it correspond to */ list_for_each_entry(dst_buf, &ctx->dst_queue, list) { u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index 47575490e74a..7964426bf2f7 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -2,7 +2,7 @@ /* * A virtual v4l2-mem2mem example device. * - * This is a virtual device driver for testing mem-to-mem videobuf framework. + * This is a virtual device driver for testing mem-to-mem vb2 framework. * It simulates a device that uses memory buffers for both source and * destination, processes the data and issues an "irq" (simulated by a delayed * workqueue). diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 240a7cc56777..b8b88244f963 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -294,7 +294,7 @@ static void airspy_urb_complete(struct urb *urb) if (unlikely(fbuf == NULL)) { s->vb_full++; dev_notice_ratelimited(s->dev, - "videobuf is full, %d packets dropped\n", + "video buffer is full, %d packets dropped\n", s->vb_full); goto skip; } diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index c0f118563c7d..eb303e94cceb 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -384,7 +384,7 @@ static void au0828_copy_video(struct au0828_dev *dev, } /* - * video-buf generic routine to get the next available buffer + * generic routine to get the next available buffer */ static inline void get_next_buf(struct au0828_dmaqueue *dma_q, struct au0828_buffer **buf) @@ -459,7 +459,7 @@ static void au0828_copy_vbi(struct au0828_dev *dev, /* - * video-buf generic routine to get the next available VBI buffer + * generic routine to get the next available VBI buffer */ static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, struct au0828_buffer **buf) diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index fdc8b7f7b0c1..33431d9f54c2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -558,7 +558,7 @@ u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, } /* - * video-buf generic routine to get the next available buffer + * generic routine to get the next available buffer */ static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, struct cx231xx_buffer **buf) diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 425e470b0fd3..e23b8ccd79d4 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -220,7 +220,7 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status) } /* - * video-buf generic routine to get the next available buffer + * generic routine to get the next available buffer */ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, struct cx231xx_buffer **buf) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 8181c0e6a25b..25e0620deff1 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -440,7 +440,7 @@ static inline void finish_buffer(struct em28xx *dev, } /* - * Copy picture data from USB buffer to videobuf buffer + * Copy picture data from USB buffer to video buffer */ static void em28xx_copy_video(struct em28xx *dev, struct em28xx_buffer *buf, @@ -521,7 +521,7 @@ static void em28xx_copy_video(struct em28xx *dev, } /* - * Copy VBI data from USB buffer to videobuf buffer + * Copy VBI data from USB buffer to video buffer */ static void em28xx_copy_vbi(struct em28xx *dev, struct em28xx_buffer *buf, diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 71de6b4c4e4c..5a1f2698efb7 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -411,7 +411,7 @@ static void msi2500_isoc_handler(struct urb *urb) if (unlikely(fbuf == NULL)) { dev->vb_full++; dev_dbg_ratelimited(dev->dev, - "videobuf is full, %d packets dropped\n", + "video buffer is full, %d packets dropped\n", dev->vb_full); continue; } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-dvb.c b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c index 6954584526a3..26811efe0fb5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c @@ -80,7 +80,7 @@ static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) static int pvr2_dvb_feed_thread(void *data) { int stat = pvr2_dvb_feed_func(data); - /* from videobuf-dvb.c: */ + while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); schedule(); -- cgit v1.3-14-g43fede From 1943fb1e50d11053ccc58932bfe1217377a5bf47 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 10 Aug 2022 13:54:45 +0200 Subject: media: staging/media: add a STAGING_MEDIA_DEPRECATED option Add a kernel config option to build deprecated media drivers that are scheduled for removal. Move stkwebcam to the deprecated directory to make it clear that this driver is deprecated. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 18 +- drivers/staging/media/Makefile | 2 +- drivers/staging/media/deprecated/stkwebcam/Kconfig | 18 + .../staging/media/deprecated/stkwebcam/Makefile | 5 + drivers/staging/media/deprecated/stkwebcam/TODO | 12 + .../media/deprecated/stkwebcam/stk-sensor.c | 587 ++++++++ .../media/deprecated/stkwebcam/stk-webcam.c | 1434 ++++++++++++++++++++ .../media/deprecated/stkwebcam/stk-webcam.h | 123 ++ drivers/staging/media/stkwebcam/Kconfig | 18 - drivers/staging/media/stkwebcam/Makefile | 5 - drivers/staging/media/stkwebcam/TODO | 12 - drivers/staging/media/stkwebcam/stk-sensor.c | 587 -------- drivers/staging/media/stkwebcam/stk-webcam.c | 1434 -------------------- drivers/staging/media/stkwebcam/stk-webcam.h | 123 -- 14 files changed, 2196 insertions(+), 2182 deletions(-) create mode 100644 drivers/staging/media/deprecated/stkwebcam/Kconfig create mode 100644 drivers/staging/media/deprecated/stkwebcam/Makefile create mode 100644 drivers/staging/media/deprecated/stkwebcam/TODO create mode 100644 drivers/staging/media/deprecated/stkwebcam/stk-sensor.c create mode 100644 drivers/staging/media/deprecated/stkwebcam/stk-webcam.c create mode 100644 drivers/staging/media/deprecated/stkwebcam/stk-webcam.h delete mode 100644 drivers/staging/media/stkwebcam/Kconfig delete mode 100644 drivers/staging/media/stkwebcam/Makefile delete mode 100644 drivers/staging/media/stkwebcam/TODO delete mode 100644 drivers/staging/media/stkwebcam/stk-sensor.c delete mode 100644 drivers/staging/media/stkwebcam/stk-webcam.c delete mode 100644 drivers/staging/media/stkwebcam/stk-webcam.h diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index ce379cae01b9..cc21b983f954 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -38,10 +38,24 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rkvdec/Kconfig" -source "drivers/staging/media/stkwebcam/Kconfig" - source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" +menuconfig STAGING_MEDIA_DEPRECATED + bool "Media staging drivers (DEPRECATED)" + default n + help + This option enables deprecated media drivers that are + scheduled for future removal from the kernel. + + If you wish to work on these drivers to prevent their removal, + then contact the linux-media@vger.kernel.org mailing list. + + If in doubt, say N here. + +if STAGING_MEDIA_DEPRECATED +source "drivers/staging/media/deprecated/stkwebcam/Kconfig" +endif + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 7ece57ca0403..804875a479be 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ -obj-$(CONFIG_VIDEO_STKWEBCAM) += stkwebcam/ +obj-$(CONFIG_VIDEO_STKWEBCAM) += deprecated/stkwebcam/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ diff --git a/drivers/staging/media/deprecated/stkwebcam/Kconfig b/drivers/staging/media/deprecated/stkwebcam/Kconfig new file mode 100644 index 000000000000..4450403dff41 --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_STKWEBCAM + tristate "USB Syntek DC1125 Camera support (DEPRECATED)" + depends on VIDEO_DEV + depends on USB + help + Say Y here if you want to use this type of camera. + Supported devices are typically found in some Asus laptops, + with USB id 174f:a311 and 05e1:0501. Other Syntek cameras + may be supported by the stk11xx driver, from which this is + derived, see + + This driver is deprecated and is scheduled for removal by + the end of 2022. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called stkwebcam. + diff --git a/drivers/staging/media/deprecated/stkwebcam/Makefile b/drivers/staging/media/deprecated/stkwebcam/Makefile new file mode 100644 index 000000000000..17ad7b6f43d0 --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +stkwebcam-objs := stk-webcam.o stk-sensor.o + +obj-$(CONFIG_VIDEO_STKWEBCAM) += stkwebcam.o + diff --git a/drivers/staging/media/deprecated/stkwebcam/TODO b/drivers/staging/media/deprecated/stkwebcam/TODO new file mode 100644 index 000000000000..735304a72729 --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/TODO @@ -0,0 +1,12 @@ +This is a very old driver for very old hardware (specifically +laptops that use this sensor). In addition according to reports +the picture quality is quite bad. + +This is also one of the few drivers still not using the vb2 +framework (or even the old videobuf framework!), so this driver +is now deprecated with the intent of removing it altogether by +the end of 2022. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/stkwebcam/stk-sensor.c b/drivers/staging/media/deprecated/stkwebcam/stk-sensor.c new file mode 100644 index 000000000000..94aa6a27f934 --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/stk-sensor.c @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) + * + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts derived from ov7670.c: + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + */ + +/* Controlling the sensor via the STK1125 vendor specific control interface: + * The camera uses an OmniVision sensor and the stk1125 provides an + * SCCB(i2c)-USB bridge which let us program the sensor. + * In my case the sensor id is 0x9652, it can be read from sensor's register + * 0x0A and 0x0B as follows: + * - read register #R: + * output #R to index 0x0208 + * output 0x0070 to index 0x0200 + * input 1 byte from index 0x0201 (some kind of status register) + * until its value is 0x01 + * input 1 byte from index 0x0209. This is the value of #R + * - write value V to register #R + * output #R to index 0x0204 + * output V to index 0x0205 + * output 0x0005 to index 0x0200 + * input 1 byte from index 0x0201 until its value becomes 0x04 + */ + +/* It seems the i2c bus is controlled with these registers */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "stk-webcam.h" + +#define STK_IIC_BASE (0x0200) +# define STK_IIC_OP (STK_IIC_BASE) +# define STK_IIC_OP_TX (0x05) +# define STK_IIC_OP_RX (0x70) +# define STK_IIC_STAT (STK_IIC_BASE+1) +# define STK_IIC_STAT_TX_OK (0x04) +# define STK_IIC_STAT_RX_OK (0x01) +/* I don't know what does this register. + * when it is 0x00 or 0x01, we cannot talk to the sensor, + * other values work */ +# define STK_IIC_ENABLE (STK_IIC_BASE+2) +# define STK_IIC_ENABLE_NO (0x00) +/* This is what the driver writes in windows */ +# define STK_IIC_ENABLE_YES (0x1e) +/* + * Address of the slave. Seems like the binary driver look for the + * sensor in multiple places, attempting a reset sequence. + * We only know about the ov9650 + */ +# define STK_IIC_ADDR (STK_IIC_BASE+3) +# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) +# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) +# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) +# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) + +#define MAX_RETRIES (50) + +#define SENSOR_ADDRESS (0x60) + +/* From ov7670.c (These registers aren't fully accurate) */ + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define COM1_QFMT 0x20 /* QVGA/QCIF format */ +#define COM1_SKIP_0 0x00 /* Do not skip any row */ +#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ +#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clock control */ +#define CLK_PLL 0x80 /* Enable internal PLL */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_SXGA 0x00 +#define COM7_FMT_VGA 0x40 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ +#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGBFIXME 0x20 /* FIXME */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + + + +/* Returns 0 if OK */ +static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) +{ + int i = 0; + u8 tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_TX_OK) { + if (tmpval) + pr_err("stk_sensor_outb failed, status=0x%02x\n", + tmpval); + return 1; + } else + return 0; +} + +static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) +{ + int i = 0; + u8 tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_RX_OK) { + if (tmpval) + pr_err("stk_sensor_inb failed, status=0x%02x\n", + tmpval); + return 1; + } + + if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) + return 1; + + *val = tmpval; + return 0; +} + +static int stk_sensor_write_regvals(struct stk_camera *dev, + struct regval *rv) +{ + int ret; + if (rv == NULL) + return 0; + while (rv->reg != 0xff || rv->val != 0xff) { + ret = stk_sensor_outb(dev, rv->reg, rv->val); + if (ret != 0) + return ret; + rv++; + } + return 0; +} + +int stk_sensor_sleep(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); +} + +int stk_sensor_wakeup(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); +} + +static struct regval ov_initvals[] = { + {REG_CLKRC, CLK_PLL}, + {REG_COM11, 0x01}, + {0x6a, 0x7d}, + {REG_AECH, 0x40}, + {REG_GAIN, 0x00}, + {REG_BLUE, 0x80}, + {REG_RED, 0x80}, + /* Do not enable fast AEC for now */ + /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ + {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, + {0x39, 0x50}, {0x38, 0x93}, + {0x37, 0x00}, {0x35, 0x81}, + {REG_COM5, 0x20}, + {REG_COM1, 0x00}, + {REG_COM3, 0x00}, + {REG_COM4, 0x00}, + {REG_PSHFT, 0x00}, + {0x16, 0x07}, + {0x33, 0xe2}, {0x34, 0xbf}, + {REG_COM16, 0x00}, + {0x96, 0x04}, + /* Gamma curve values */ +/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, +*/ + {REG_GFIX, 0x40}, + {0x8e, 0x00}, + {REG_COM12, 0x73}, + {0x8f, 0xdf}, {0x8b, 0x06}, + {0x8c, 0x20}, + {0x94, 0x88}, {0x95, 0x88}, +/* {REG_COM15, 0xc1}, TODO */ + {0x29, 0x3f}, + {REG_COM6, 0x42}, + {REG_BD50MAX, 0x80}, + {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, + {REG_BD60MAX, 0x0a}, + {0x90, 0x00}, {0x91, 0x00}, + {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, + {REG_AEW, 0x68}, {REG_AEB, 0x5c}, + {REG_VPT, 0xc3}, + {REG_COM9, 0x2e}, + {0x2a, 0x00}, {0x2b, 0x00}, + + {0xff, 0xff}, /* END MARKER */ +}; + +/* Probe the I2C bus and initialise the sensor chip */ +int stk_sensor_init(struct stk_camera *dev) +{ + u8 idl = 0; + u8 idh = 0; + + if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) + || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) + || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { + pr_err("Sensor resetting failed\n"); + return -ENODEV; + } + msleep(10); + /* Read the manufacturer ID: ov = 0x7FA2 */ + if (stk_sensor_inb(dev, REG_MIDH, &idh) + || stk_sensor_inb(dev, REG_MIDL, &idl)) { + pr_err("Strange error reading sensor ID\n"); + return -ENODEV; + } + if (idh != 0x7f || idl != 0xa2) { + pr_err("Huh? you don't have a sensor from ovt\n"); + return -ENODEV; + } + if (stk_sensor_inb(dev, REG_PID, &idh) + || stk_sensor_inb(dev, REG_VER, &idl)) { + pr_err("Could not read sensor model\n"); + return -ENODEV; + } + stk_sensor_write_regvals(dev, ov_initvals); + msleep(10); + pr_info("OmniVision sensor detected, id %02X%02X at address %x\n", + idh, idl, SENSOR_ADDRESS); + return 0; +} + +/* V4L2_PIX_FMT_UYVY */ +static struct regval ov_fmt_uyvy[] = { + {REG_TSLB, TSLB_YLAST|0x08 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* V4L2_PIX_FMT_YUYV */ +static struct regval ov_fmt_yuyv[] = { + {REG_TSLB, 0 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; + +/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ +static struct regval ov_fmt_rgbr[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, 0x00}, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ +static struct regval ov_fmt_rgbp[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, TSLB_BYTEORD }, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_SRGGB8 */ +static struct regval ov_fmt_bayer[] = { + /* This changes color order */ + {REG_TSLB, 0x40}, /* BGGR */ + /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* + * Store a set of start/stop values into the camera. + */ +static int stk_sensor_set_hw(struct stk_camera *dev, + int hstart, int hstop, int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_HREF, v); +/* + * Vertical: similar arrangement (note: this is different from ov7670.c) + */ + ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_VREF, &v); + v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_VREF, v); + return ret; +} + + +int stk_sensor_configure(struct stk_camera *dev) +{ + int com7; + /* + * We setup the sensor to output dummy lines in low-res modes, + * so we don't get absurdly hight framerates. + */ + unsigned dummylines; + int flip; + struct regval *rv; + + switch (dev->vsettings.mode) { + case MODE_QCIF: com7 = COM7_FMT_QCIF; + dummylines = 604; + break; + case MODE_QVGA: com7 = COM7_FMT_QVGA; + dummylines = 267; + break; + case MODE_CIF: com7 = COM7_FMT_CIF; + dummylines = 412; + break; + case MODE_VGA: com7 = COM7_FMT_VGA; + dummylines = 11; + break; + case MODE_SXGA: com7 = COM7_FMT_SXGA; + dummylines = 0; + break; + default: + pr_err("Unsupported mode %d\n", dev->vsettings.mode); + return -EFAULT; + } + switch (dev->vsettings.palette) { + case V4L2_PIX_FMT_UYVY: + com7 |= COM7_YUV; + rv = ov_fmt_uyvy; + break; + case V4L2_PIX_FMT_YUYV: + com7 |= COM7_YUV; + rv = ov_fmt_yuyv; + break; + case V4L2_PIX_FMT_RGB565: + com7 |= COM7_RGB; + rv = ov_fmt_rgbp; + break; + case V4L2_PIX_FMT_RGB565X: + com7 |= COM7_RGB; + rv = ov_fmt_rgbr; + break; + case V4L2_PIX_FMT_SBGGR8: + com7 |= COM7_PBAYER; + rv = ov_fmt_bayer; + break; + default: + pr_err("Unsupported colorspace\n"); + return -EFAULT; + } + /*FIXME sometimes the sensor go to a bad state + stk_sensor_write_regvals(dev, ov_initvals); */ + stk_sensor_outb(dev, REG_COM7, com7); + msleep(50); + stk_sensor_write_regvals(dev, rv); + flip = (dev->vsettings.vflip?MVFP_FLIP:0) + | (dev->vsettings.hflip?MVFP_MIRROR:0); + stk_sensor_outb(dev, REG_MVFP, flip); + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 + && !dev->vsettings.vflip) + stk_sensor_outb(dev, REG_TSLB, 0x08); + stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); + stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); + msleep(50); + switch (dev->vsettings.mode) { + case MODE_VGA: + if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) + pr_err("stk_sensor_set_hw failed (VGA)\n"); + break; + case MODE_SXGA: + case MODE_CIF: + case MODE_QVGA: + case MODE_QCIF: + /*FIXME These settings seem ignored by the sensor + if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) + pr_err("stk_sensor_set_hw failed (SXGA)\n"); + */ + break; + } + msleep(10); + return 0; +} + +int stk_sensor_set_brightness(struct stk_camera *dev, int br) +{ + if (br < 0 || br > 0xff) + return -EINVAL; + stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); + stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); + return 0; +} + diff --git a/drivers/staging/media/deprecated/stkwebcam/stk-webcam.c b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.c new file mode 100644 index 000000000000..787edb3d47c2 --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.c @@ -0,0 +1,1434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stk-webcam.c : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts are inspired from cafe_ccic.c + * Copyright 2006-2007 Jonathan Corbet + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stk-webcam.h" + + +static int hflip = -1; +module_param(hflip, int, 0444); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); + +static int vflip = -1; +module_param(vflip, int, 0444); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); +MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); + +/* Some cameras have audio interfaces, we aren't interested in those */ +static const struct usb_device_id stkwebcam_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, + { } +}; +MODULE_DEVICE_TABLE(usb, stkwebcam_table); + +/* + * The stk webcam laptop module is mounted upside down in some laptops :( + * + * Some background information (thanks to Hans de Goede for providing this): + * + * 1) Once upon a time the stkwebcam driver was written + * + * 2) The webcam in question was used mostly in Asus laptop models, including + * the laptop of the original author of the driver, and in these models, in + * typical Asus fashion (see the long long list for uvc cams inside v4l-utils), + * they mounted the webcam-module the wrong way up. So the hflip and vflip + * module options were given a default value of 1 (the correct value for + * upside down mounted models) + * + * 3) Years later I got a bug report from a user with a laptop with stkwebcam, + * where the module was actually mounted the right way up, and thus showed + * upside down under Linux. So now I was facing the choice of 2 options: + * + * a) Add a not-upside-down list to stkwebcam, which overrules the default. + * + * b) Do it like all the other drivers do, and make the default right for + * cams mounted the proper way and add an upside-down model list, with + * models where we need to flip-by-default. + * + * Despite knowing that going b) would cause a period of pain where we were + * building the table I opted to go for option b), since a) is just too ugly, + * and worse different from how every other driver does it leading to + * confusion in the long run. This change was made in kernel 3.6. + * + * So for any user report about upside-down images since kernel 3.6 ask them + * to provide the output of 'sudo dmidecode' so the laptop can be added in + * the table below. + */ +static const struct dmi_system_id stk_upside_down_dmi_table[] = { + { + .ident = "ASUS G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G1") + } + }, { + .ident = "ASUS F3JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "F3JC") + } + }, + { + .ident = "T12Rg-H", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"), + DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H") + } + }, + { + .ident = "ASUS A6VM", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + } + }, + { + .ident = "ASUS A6JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + } + }, + {} +}; + + +/* + * Basic stuff + */ +int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) + return ret; + else + return 0; +} + +int stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + &dev->read_reg_scratch, + sizeof(u8), + 500); + if (ret >= 0) + *value = dev->read_reg_scratch; + + if (ret < 0) + return ret; + else + return 0; +} + +static int stk_start_stream(struct stk_camera *dev) +{ + u8 value; + int i, ret; + u8 value_116, value_117; + + + if (!is_present(dev)) + return -ENODEV; + if (!is_memallocd(dev) || !is_initialised(dev)) { + pr_err("FIXME: Buffers are not allocated\n"); + return -EFAULT; + } + ret = usb_set_interface(dev->udev, 0, 5); + + if (ret < 0) + pr_err("usb_set_interface failed !\n"); + if (stk_sensor_wakeup(dev)) + pr_err("error awaking the sensor\n"); + + stk_camera_read_reg(dev, 0x0116, &value_116); + stk_camera_read_reg(dev, 0x0117, &value_117); + + stk_camera_write_reg(dev, 0x0116, 0x0000); + stk_camera_write_reg(dev, 0x0117, 0x0000); + + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value | 0x80); + + stk_camera_write_reg(dev, 0x0116, value_116); + stk_camera_write_reg(dev, 0x0117, value_117); + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) { + ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); + atomic_inc(&dev->urbs_used); + if (ret) + return ret; + } + } + set_streaming(dev); + return 0; +} + +static int stk_stop_stream(struct stk_camera *dev) +{ + u8 value; + int i; + if (is_present(dev)) { + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value & ~0x80); + if (dev->isobufs != NULL) { + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) + usb_kill_urb(dev->isobufs[i].urb); + } + } + unset_streaming(dev); + + if (usb_set_interface(dev->udev, 0, 0)) + pr_err("usb_set_interface failed !\n"); + if (stk_sensor_sleep(dev)) + pr_err("error suspending the sensor\n"); + } + return 0; +} + +/* + * This seems to be the shortest init sequence we + * must do in order to find the sensor + * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor + * is also reset. Maybe powers down it? + * Rest of values don't make a difference + */ + +static struct regval stk1125_initvals[] = { + /*TODO: What means this sequence? */ + {0x0000, 0x24}, + {0x0100, 0x21}, + {0x0002, 0x68}, + {0x0003, 0x80}, + {0x0005, 0x00}, + {0x0007, 0x03}, + {0x000d, 0x00}, + {0x000f, 0x02}, + {0x0300, 0x12}, + {0x0350, 0x41}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0018, 0x10}, + {0x0019, 0x00}, + {0x001b, 0x0e}, + {0x001c, 0x46}, + {0x0300, 0x80}, + {0x001a, 0x04}, + {0x0110, 0x00}, + {0x0111, 0x00}, + {0x0112, 0x00}, + {0x0113, 0x00}, + + {0xffff, 0xff}, +}; + + +static int stk_initialise(struct stk_camera *dev) +{ + struct regval *rv; + int ret; + if (!is_present(dev)) + return -ENODEV; + if (is_initialised(dev)) + return 0; + rv = stk1125_initvals; + while (rv->reg != 0xffff) { + ret = stk_camera_write_reg(dev, rv->reg, rv->val); + if (ret) + return ret; + rv++; + } + if (stk_sensor_init(dev) == 0) { + set_initialised(dev); + return 0; + } else + return -1; +} + +/* *********************************************** */ +/* + * This function is called as an URB transfert is complete (Isochronous pipe). + * So, the traitement is done in interrupt time, so it has be fast, not crash, + * and not stall. Neat. + */ +static void stk_isoc_handler(struct urb *urb) +{ + int i; + int ret; + int framelen; + unsigned long flags; + + unsigned char *fill = NULL; + unsigned char *iso_buf = NULL; + + struct stk_camera *dev; + struct stk_sio_buffer *fb; + + dev = (struct stk_camera *) urb->context; + + if (dev == NULL) { + pr_err("isoc_handler called with NULL device !\n"); + return; + } + + if (urb->status == -ENOENT || urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN) { + atomic_dec(&dev->urbs_used); + return; + } + + spin_lock_irqsave(&dev->spinlock, flags); + + if (urb->status != -EINPROGRESS && urb->status != 0) { + pr_err("isoc_handler: urb->status == %d\n", urb->status); + goto resubmit; + } + + if (list_empty(&dev->sio_avail)) { + /*FIXME Stop streaming after a while */ + pr_err_ratelimited("isoc_handler without available buffer!\n"); + goto resubmit; + } + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fill = fb->buffer + fb->v4lbuf.bytesused; + + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status != 0) { + if (urb->iso_frame_desc[i].status != -EXDEV) + pr_err("Frame %d has error %d\n", + i, urb->iso_frame_desc[i].status); + continue; + } + framelen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (framelen <= 4) + continue; /* no data */ + + /* + * we found something informational from there + * the isoc frames have to type of headers + * type1: 00 xx 00 00 or 20 xx 00 00 + * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 + * xx is a sequencer which has never been seen over 0x3f + * imho data written down looks like bayer, i see similarities + * after every 640 bytes + */ + if (*iso_buf & 0x80) { + framelen -= 8; + iso_buf += 8; + /* This marks a new frame */ + if (fb->v4lbuf.bytesused != 0 + && fb->v4lbuf.bytesused != dev->frame_size) { + pr_err_ratelimited("frame %d, bytesused=%d, skipping\n", + i, fb->v4lbuf.bytesused); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else if (fb->v4lbuf.bytesused == dev->frame_size) { + if (list_is_singular(&dev->sio_avail)) { + /* Always reuse the last buffer */ + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else { + list_move_tail(dev->sio_avail.next, + &dev->sio_full); + wake_up(&dev->wait_frame); + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } + } + } else { + framelen -= 4; + iso_buf += 4; + } + + /* Our buffer is full !!! */ + if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { + pr_err_ratelimited("Frame buffer overflow, lost sync\n"); + /*FIXME Do something here? */ + continue; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + memcpy(fill, iso_buf, framelen); + spin_lock_irqsave(&dev->spinlock, flags); + fill += framelen; + + /* New size of our buffer */ + fb->v4lbuf.bytesused += framelen; + } + +resubmit: + spin_unlock_irqrestore(&dev->spinlock, flags); + urb->dev = dev->udev; + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0) { + pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n", + ret); + } +} + +/* -------------------------------------------- */ + +static int stk_prepare_iso(struct stk_camera *dev) +{ + void *kbuf; + int i, j; + struct urb *urb; + struct usb_device *udev; + + if (dev == NULL) + return -ENXIO; + udev = dev->udev; + + if (dev->isobufs) + pr_err("isobufs already allocated. Bad\n"); + else + dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), + GFP_KERNEL); + if (dev->isobufs == NULL) { + pr_err("Unable to allocate iso buffers\n"); + return -ENOMEM; + } + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].data == NULL) { + kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + pr_err("Failed to allocate iso buffer %d\n", i); + goto isobufs_out; + } + dev->isobufs[i].data = kbuf; + } else + pr_err("isobuf data already allocated\n"); + if (dev->isobufs[i].urb == NULL) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) + goto isobufs_out; + dev->isobufs[i].urb = urb; + } else { + pr_err("Killing URB\n"); + usb_kill_urb(dev->isobufs[i].urb); + urb = dev->isobufs[i].urb; + } + urb->interval = 1; + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->isobufs[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = stk_isoc_handler; + urb->context = dev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; + } + } + set_memallocd(dev); + return 0; + +isobufs_out: + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) + kfree(dev->isobufs[i].data); + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) + usb_free_urb(dev->isobufs[i].urb); + kfree(dev->isobufs); + dev->isobufs = NULL; + return -ENOMEM; +} + +static void stk_clean_iso(struct stk_camera *dev) +{ + int i; + + if (dev == NULL || dev->isobufs == NULL) + return; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = dev->isobufs[i].urb; + if (urb) { + if (atomic_read(&dev->urbs_used) && is_present(dev)) + usb_kill_urb(urb); + usb_free_urb(urb); + } + kfree(dev->isobufs[i].data); + } + kfree(dev->isobufs); + dev->isobufs = NULL; + unset_memallocd(dev); +} + +static int stk_setup_siobuf(struct stk_camera *dev, int index) +{ + struct stk_sio_buffer *buf = dev->sio_bufs + index; + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->dev = dev; + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int stk_free_sio_buffers(struct stk_camera *dev) +{ + int i; + int nbufs; + unsigned long flags; + if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) + return 0; + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].mapcount > 0) + return -EBUSY; + } + /* + * OK, let's do it. + */ + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + nbufs = dev->n_sbufs; + dev->n_sbufs = 0; + spin_unlock_irqrestore(&dev->spinlock, flags); + for (i = 0; i < nbufs; i++) + vfree(dev->sio_bufs[i].buffer); + kfree(dev->sio_bufs); + dev->sio_bufs = NULL; + return 0; +} + +static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int i; + if (dev->sio_bufs != NULL) + pr_err("sio_bufs already allocated\n"); + else { + dev->sio_bufs = kcalloc(n_sbufs, + sizeof(struct stk_sio_buffer), + GFP_KERNEL); + if (dev->sio_bufs == NULL) + return -ENOMEM; + for (i = 0; i < n_sbufs; i++) { + if (stk_setup_siobuf(dev, i)) + return (dev->n_sbufs > 1 ? 0 : -ENOMEM); + dev->n_sbufs = i+1; + } + } + return 0; +} + +static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int err; + err = stk_prepare_iso(dev); + if (err) { + stk_clean_iso(dev); + return err; + } + err = stk_prepare_sio_buffers(dev, n_sbufs); + if (err) { + stk_free_sio_buffers(dev); + return err; + } + return 0; +} + +static void stk_free_buffers(struct stk_camera *dev) +{ + stk_clean_iso(dev); + stk_free_sio_buffers(dev); +} +/* -------------------------------------------- */ + +/* v4l file operations */ + +static int v4l_stk_open(struct file *fp) +{ + struct stk_camera *dev = video_drvdata(fp); + int err; + + if (dev == NULL || !is_present(dev)) + return -ENXIO; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (!dev->first_init) + stk_camera_write_reg(dev, 0x0, 0x24); + else + dev->first_init = 0; + + err = v4l2_fh_open(fp); + if (!err) + usb_autopm_get_interface(dev->interface); + mutex_unlock(&dev->lock); + return err; +} + +static int v4l_stk_release(struct file *fp) +{ + struct stk_camera *dev = video_drvdata(fp); + + mutex_lock(&dev->lock); + if (dev->owner == fp) { + stk_stop_stream(dev); + stk_free_buffers(dev); + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); + dev->owner = NULL; + } + + usb_autopm_put_interface(dev->interface); + mutex_unlock(&dev->lock); + return v4l2_fh_release(fp); +} + +static ssize_t stk_read(struct file *fp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int i; + int ret; + unsigned long flags; + struct stk_sio_buffer *sbuf; + struct stk_camera *dev = video_drvdata(fp); + + if (!is_present(dev)) + return -EIO; + if (dev->owner && (!dev->reading || dev->owner != fp)) + return -EBUSY; + dev->owner = fp; + if (!is_streaming(dev)) { + if (stk_initialise(dev) + || stk_allocate_buffers(dev, 3) + || stk_start_stream(dev)) + return -ENOMEM; + dev->reading = 1; + spin_lock_irqsave(&dev->spinlock, flags); + for (i = 0; i < dev->n_sbufs; i++) { + list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); + dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + } + if (*f_pos == 0) { + if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + } + if (count + *f_pos > dev->frame_size) + count = dev->frame_size - *f_pos; + spin_lock_irqsave(&dev->spinlock, flags); + if (list_empty(&dev->sio_full)) { + spin_unlock_irqrestore(&dev->spinlock, flags); + pr_err("BUG: No siobufs ready\n"); + return 0; + } + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + spin_unlock_irqrestore(&dev->spinlock, flags); + + if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) + return -EFAULT; + + *f_pos += count; + + if (*f_pos >= dev->frame_size) { + *f_pos = 0; + spin_lock_irqsave(&dev->spinlock, flags); + list_move_tail(&sbuf->list, &dev->sio_avail); + spin_unlock_irqrestore(&dev->spinlock, flags); + } + return count; +} + +static ssize_t v4l_stk_read(struct file *fp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct stk_camera *dev = video_drvdata(fp); + int ret; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = stk_read(fp, buf, count, f_pos); + mutex_unlock(&dev->lock); + return ret; +} + +static __poll_t v4l_stk_poll(struct file *fp, poll_table *wait) +{ + struct stk_camera *dev = video_drvdata(fp); + __poll_t res = v4l2_ctrl_poll(fp, wait); + + poll_wait(fp, &dev->wait_frame, wait); + + if (!is_present(dev)) + return EPOLLERR; + + if (!list_empty(&dev->sio_full)) + return res | EPOLLIN | EPOLLRDNORM; + + return res; +} + + +static void stk_v4l_vm_open(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount++; +} +static void stk_v4l_vm_close(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount--; + if (sbuf->mapcount == 0) + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; +} +static const struct vm_operations_struct stk_v4l_vm_ops = { + .open = stk_v4l_vm_open, + .close = stk_v4l_vm_close +}; + +static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) +{ + unsigned int i; + int ret; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct stk_camera *dev = video_drvdata(fp); + struct stk_sio_buffer *sbuf = NULL; + + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { + sbuf = dev->sio_bufs + i; + break; + } + } + if (sbuf == NULL) + return -EINVAL; + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + return ret; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &stk_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + stk_v4l_vm_open(vma); + return 0; +} + +/* v4l ioctl handlers */ + +static int stk_vidioc_querycap(struct file *filp, + void *priv, struct v4l2_capability *cap) +{ + struct stk_camera *dev = video_drvdata(filp); + + strscpy(cap->driver, "stk", sizeof(cap->driver)); + strscpy(cap->card, "stk", sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + return 0; +} + +static int stk_vidioc_enum_input(struct file *filp, + void *priv, struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + strscpy(input->name, "Syntek USB Camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + + +static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int stk_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct stk_camera *dev = + container_of(ctrl->handler, struct stk_camera, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return stk_sensor_set_brightness(dev, ctrl->val); + case V4L2_CID_HFLIP: + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = !ctrl->val; + else + dev->vsettings.hflip = ctrl->val; + return 0; + case V4L2_CID_VFLIP: + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = !ctrl->val; + else + dev->vsettings.vflip = ctrl->val; + return 0; + default: + return -EINVAL; + } + return 0; +} + + +static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmtd) +{ + switch (fmtd->index) { + case 0: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565; + break; + case 1: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; + break; + case 2: + fmtd->pixelformat = V4L2_PIX_FMT_UYVY; + break; + case 3: + fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case 4: + fmtd->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + return 0; +} + +static struct stk_size { + unsigned w; + unsigned h; + enum stk_mode m; +} stk_sizes[] = { + { .w = 1280, .h = 1024, .m = MODE_SXGA, }, + { .w = 640, .h = 480, .m = MODE_VGA, }, + { .w = 352, .h = 288, .m = MODE_CIF, }, + { .w = 320, .h = 240, .m = MODE_QVGA, }, + { .w = 176, .h = 144, .m = MODE_QCIF, }, +}; + +static int stk_vidioc_g_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix_format = &f->fmt.pix; + struct stk_camera *dev = video_drvdata(filp); + int i; + + for (i = 0; i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode; i++) + ; + if (i == ARRAY_SIZE(stk_sizes)) { + pr_err("ERROR: mode invalid\n"); + return -EINVAL; + } + pix_format->width = stk_sizes[i].w; + pix_format->height = stk_sizes[i].h; + pix_format->field = V4L2_FIELD_NONE; + pix_format->colorspace = V4L2_COLORSPACE_SRGB; + pix_format->pixelformat = dev->vsettings.palette; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + pix_format->bytesperline = pix_format->width; + else + pix_format->bytesperline = 2 * pix_format->width; + pix_format->sizeimage = pix_format->bytesperline + * pix_format->height; + return 0; +} + +static int stk_try_fmt_vid_cap(struct file *filp, + struct v4l2_format *fmtd, int *idx) +{ + int i; + switch (fmtd->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + break; + default: + return -EINVAL; + } + for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { + if (fmtd->fmt.pix.width > stk_sizes[i].w) + break; + } + if (i == ARRAY_SIZE(stk_sizes) + || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) + < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { + fmtd->fmt.pix.height = stk_sizes[i-1].h; + fmtd->fmt.pix.width = stk_sizes[i-1].w; + if (idx) + *idx = i - 1; + } else { + fmtd->fmt.pix.height = stk_sizes[i].h; + fmtd->fmt.pix.width = stk_sizes[i].w; + if (idx) + *idx = i; + } + + fmtd->fmt.pix.field = V4L2_FIELD_NONE; + fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; + else + fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; + fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline + * fmtd->fmt.pix.height; + return 0; +} + +static int stk_vidioc_try_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + return stk_try_fmt_vid_cap(filp, fmtd, NULL); +} + +static int stk_setup_format(struct stk_camera *dev) +{ + int i = 0; + int depth; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + depth = 1; + else + depth = 2; + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) + i++; + if (i == ARRAY_SIZE(stk_sizes)) { + pr_err("Something is broken in %s\n", __func__); + return -EFAULT; + } + /* This registers controls some timings, not sure of what. */ + stk_camera_write_reg(dev, 0x001b, 0x0e); + if (dev->vsettings.mode == MODE_SXGA) + stk_camera_write_reg(dev, 0x001c, 0x0e); + else + stk_camera_write_reg(dev, 0x001c, 0x46); + /* + * Registers 0x0115 0x0114 are the size of each line (bytes), + * regs 0x0117 0x0116 are the height of the image. + */ + stk_camera_write_reg(dev, 0x0115, + ((stk_sizes[i].w * depth) >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0114, + (stk_sizes[i].w * depth) & 0xff); + stk_camera_write_reg(dev, 0x0117, + (stk_sizes[i].h >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0116, + stk_sizes[i].h & 0xff); + return stk_sensor_configure(dev); +} + +static int stk_vidioc_s_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + int ret; + int idx; + struct stk_camera *dev = video_drvdata(filp); + + if (dev == NULL) + return -ENODEV; + if (!is_present(dev)) + return -ENODEV; + if (is_streaming(dev)) + return -EBUSY; + if (dev->owner) + return -EBUSY; + ret = stk_try_fmt_vid_cap(filp, fmtd, &idx); + if (ret) + return ret; + + dev->vsettings.palette = fmtd->fmt.pix.pixelformat; + stk_free_buffers(dev); + dev->frame_size = fmtd->fmt.pix.sizeimage; + dev->vsettings.mode = stk_sizes[idx].m; + + stk_initialise(dev); + return stk_setup_format(dev); +} + +static int stk_vidioc_reqbufs(struct file *filp, + void *priv, struct v4l2_requestbuffers *rb) +{ + struct stk_camera *dev = video_drvdata(filp); + + if (dev == NULL) + return -ENODEV; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (is_streaming(dev) + || (dev->owner && dev->owner != filp)) + return -EBUSY; + stk_free_buffers(dev); + if (rb->count == 0) { + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); + dev->owner = NULL; + return 0; + } + dev->owner = filp; + + /*FIXME If they ask for zero, we must stop streaming and free */ + if (rb->count < 3) + rb->count = 3; + /* Arbitrary limit */ + else if (rb->count > 5) + rb->count = 5; + + stk_allocate_buffers(dev, rb->count); + rb->count = dev->n_sbufs; + return 0; +} + +static int stk_vidioc_querybuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = video_drvdata(filp); + struct stk_sio_buffer *sbuf; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_qbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = video_drvdata(filp); + struct stk_sio_buffer *sbuf; + unsigned long flags; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) + return 0; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&dev->spinlock, flags); + list_add_tail(&sbuf->list, &dev->sio_avail); + *buf = sbuf->v4lbuf; + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + +static int stk_vidioc_dqbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = video_drvdata(filp); + struct stk_sio_buffer *sbuf; + unsigned long flags; + int ret; + + if (!is_streaming(dev)) + return -EINVAL; + + if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + + spin_lock_irqsave(&dev->spinlock, flags); + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&dev->spinlock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + sbuf->v4lbuf.sequence = ++dev->sequence; + v4l2_buffer_set_timestamp(&sbuf->v4lbuf, ktime_get_ns()); + + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_streamon(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = video_drvdata(filp); + if (is_streaming(dev)) + return 0; + if (dev->sio_bufs == NULL) + return -EINVAL; + dev->sequence = 0; + return stk_start_stream(dev); +} + +static int stk_vidioc_streamoff(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = video_drvdata(filp); + unsigned long flags; + int i; + stk_stop_stream(dev); + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + for (i = 0; i < dev->n_sbufs; i++) { + INIT_LIST_HEAD(&dev->sio_bufs[i].list); + dev->sio_bufs[i].v4lbuf.flags = 0; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + + +static int stk_vidioc_g_parm(struct file *filp, + void *priv, struct v4l2_streamparm *sp) +{ + /*FIXME This is not correct */ + sp->parm.capture.timeperframe.numerator = 1; + sp->parm.capture.timeperframe.denominator = 30; + sp->parm.capture.readbuffers = 2; + return 0; +} + +static int stk_vidioc_enum_framesizes(struct file *filp, + void *priv, struct v4l2_frmsizeenum *frms) +{ + if (frms->index >= ARRAY_SIZE(stk_sizes)) + return -EINVAL; + switch (frms->pixel_format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = stk_sizes[frms->index].w; + frms->discrete.height = stk_sizes[frms->index].h; + return 0; + default: return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops stk_ctrl_ops = { + .s_ctrl = stk_s_ctrl, +}; + +static const struct v4l2_file_operations v4l_stk_fops = { + .owner = THIS_MODULE, + .open = v4l_stk_open, + .release = v4l_stk_release, + .read = v4l_stk_read, + .poll = v4l_stk_poll, + .mmap = v4l_stk_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { + .vidioc_querycap = stk_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = stk_vidioc_enum_input, + .vidioc_s_input = stk_vidioc_s_input, + .vidioc_g_input = stk_vidioc_g_input, + .vidioc_reqbufs = stk_vidioc_reqbufs, + .vidioc_querybuf = stk_vidioc_querybuf, + .vidioc_qbuf = stk_vidioc_qbuf, + .vidioc_dqbuf = stk_vidioc_dqbuf, + .vidioc_streamon = stk_vidioc_streamon, + .vidioc_streamoff = stk_vidioc_streamoff, + .vidioc_g_parm = stk_vidioc_g_parm, + .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void stk_v4l_dev_release(struct video_device *vd) +{ + struct stk_camera *dev = vdev_to_camera(vd); + + if (dev->sio_bufs != NULL || dev->isobufs != NULL) + pr_err("We are leaking memory\n"); + usb_put_intf(dev->interface); + usb_put_dev(dev->udev); + + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); +} + +static const struct video_device stk_v4l_data = { + .name = "stkwebcam", + .fops = &v4l_stk_fops, + .ioctl_ops = &v4l_stk_ioctl_ops, + .release = stk_v4l_dev_release, +}; + + +static int stk_register_video_device(struct stk_camera *dev) +{ + int err; + + dev->vdev = stk_v4l_data; + dev->vdev.lock = &dev->lock; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + video_set_drvdata(&dev->vdev, dev); + err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); + if (err) + pr_err("v4l registration failed\n"); + else + pr_info("Syntek USB2.0 Camera is now controlling device %s\n", + video_device_node_name(&dev->vdev)); + return err; +} + + +/* USB Stuff */ + +static int stk_camera_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct v4l2_ctrl_handler *hdl; + int err = 0; + int i; + + struct stk_camera *dev = NULL; + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + + dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); + if (dev == NULL) { + pr_err("Out of memory !\n"); + return -ENOMEM; + } + err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + kfree(dev); + return err; + } + hdl = &dev->hdl; + v4l2_ctrl_handler_init(hdl, 3); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xff, 0x1, 0x60); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 1); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto error; + } + dev->v4l2_dev.ctrl_handler = hdl; + + spin_lock_init(&dev->spinlock); + mutex_init(&dev->lock); + init_waitqueue_head(&dev->wait_frame); + dev->first_init = 1; /* webcam LED management */ + + dev->udev = usb_get_dev(udev); + dev->interface = interface; + usb_get_intf(interface); + + if (hflip != -1) + dev->vsettings.hflip = hflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = 1; + else + dev->vsettings.hflip = 0; + if (vflip != -1) + dev->vsettings.vflip = vflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = 1; + else + dev->vsettings.vflip = 0; + dev->n_sbufs = 0; + set_present(dev); + + /* Set up the endpoint information + * use only the first isoc-in endpoint + * for the current alternate setting */ + iface_desc = interface->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->isoc_ep + && usb_endpoint_is_isoc_in(endpoint)) { + /* we found an isoc in endpoint */ + dev->isoc_ep = usb_endpoint_num(endpoint); + break; + } + } + if (!dev->isoc_ep) { + pr_err("Could not find isoc-in endpoint\n"); + err = -ENODEV; + goto error_put; + } + dev->vsettings.palette = V4L2_PIX_FMT_RGB565; + dev->vsettings.mode = MODE_VGA; + dev->frame_size = 640 * 480 * 2; + + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + + usb_set_intfdata(interface, dev); + + err = stk_register_video_device(dev); + if (err) + goto error_put; + + return 0; + +error_put: + usb_put_intf(interface); + usb_put_dev(dev->udev); +error: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return err; +} + +static void stk_camera_disconnect(struct usb_interface *interface) +{ + struct stk_camera *dev = usb_get_intfdata(interface); + + usb_set_intfdata(interface, NULL); + unset_present(dev); + + wake_up_interruptible(&dev->wait_frame); + + pr_info("Syntek USB2.0 Camera release resources device %s\n", + video_device_node_name(&dev->vdev)); + + video_unregister_device(&dev->vdev); +} + +#ifdef CONFIG_PM +static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (is_streaming(dev)) { + stk_stop_stream(dev); + /* yes, this is ugly */ + set_streaming(dev); + } + return 0; +} + +static int stk_camera_resume(struct usb_interface *intf) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (!is_initialised(dev)) + return 0; + unset_initialised(dev); + stk_initialise(dev); + stk_camera_write_reg(dev, 0x0, 0x49); + stk_setup_format(dev); + if (is_streaming(dev)) + stk_start_stream(dev); + return 0; +} +#endif + +static struct usb_driver stk_camera_driver = { + .name = "stkwebcam", + .probe = stk_camera_probe, + .disconnect = stk_camera_disconnect, + .id_table = stkwebcam_table, +#ifdef CONFIG_PM + .suspend = stk_camera_suspend, + .resume = stk_camera_resume, +#endif +}; + +module_usb_driver(stk_camera_driver); diff --git a/drivers/staging/media/deprecated/stkwebcam/stk-webcam.h b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.h new file mode 100644 index 000000000000..136decffe9ce --- /dev/null +++ b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * stk-webcam.h : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + */ + +#ifndef STKWEBCAM_H +#define STKWEBCAM_H + +#include +#include +#include +#include + +#define DRIVER_VERSION "v0.0.1" +#define DRIVER_VERSION_NUM 0x000001 + +#define MAX_ISO_BUFS 3 +#define ISO_FRAMES_PER_DESC 16 +#define ISO_MAX_FRAME_SIZE 3 * 1024 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +struct stk_iso_buf { + void *data; + int length; + int read; + struct urb *urb; +}; + +/* Streaming IO buffers */ +struct stk_sio_buffer { + struct v4l2_buffer v4lbuf; + char *buffer; + int mapcount; + struct stk_camera *dev; + struct list_head list; +}; + +enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; + +struct stk_video { + enum stk_mode mode; + __u32 palette; + int hflip; + int vflip; +}; + +enum stk_status { + S_PRESENT = 1, + S_INITIALISED = 2, + S_MEMALLOCD = 4, + S_STREAMING = 8, +}; +#define is_present(dev) ((dev)->status & S_PRESENT) +#define is_initialised(dev) ((dev)->status & S_INITIALISED) +#define is_streaming(dev) ((dev)->status & S_STREAMING) +#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) +#define set_present(dev) ((dev)->status = S_PRESENT) +#define unset_present(dev) ((dev)->status &= \ + ~(S_PRESENT|S_INITIALISED|S_STREAMING)) +#define set_initialised(dev) ((dev)->status |= S_INITIALISED) +#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) +#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) +#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) +#define set_streaming(dev) ((dev)->status |= S_STREAMING) +#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) + +struct regval { + unsigned reg; + unsigned val; +}; + +struct stk_camera { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct video_device vdev; + struct usb_device *udev; + struct usb_interface *interface; + int webcam_model; + struct file *owner; + struct mutex lock; + int first_init; + + u8 isoc_ep; + + /* Not sure if this is right */ + atomic_t urbs_used; + + struct stk_video vsettings; + + enum stk_status status; + + spinlock_t spinlock; + wait_queue_head_t wait_frame; + + struct stk_iso_buf *isobufs; + + int frame_size; + /* Streaming buffers */ + int reading; + unsigned int n_sbufs; + struct stk_sio_buffer *sio_bufs; + struct list_head sio_avail; + struct list_head sio_full; + unsigned sequence; + + u8 read_reg_scratch; +}; + +#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) + +int stk_camera_write_reg(struct stk_camera *, u16, u8); +int stk_camera_read_reg(struct stk_camera *, u16, u8 *); + +int stk_sensor_init(struct stk_camera *); +int stk_sensor_configure(struct stk_camera *); +int stk_sensor_sleep(struct stk_camera *dev); +int stk_sensor_wakeup(struct stk_camera *dev); +int stk_sensor_set_brightness(struct stk_camera *dev, int br); + +#endif diff --git a/drivers/staging/media/stkwebcam/Kconfig b/drivers/staging/media/stkwebcam/Kconfig deleted file mode 100644 index 4450403dff41..000000000000 --- a/drivers/staging/media/stkwebcam/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_STKWEBCAM - tristate "USB Syntek DC1125 Camera support (DEPRECATED)" - depends on VIDEO_DEV - depends on USB - help - Say Y here if you want to use this type of camera. - Supported devices are typically found in some Asus laptops, - with USB id 174f:a311 and 05e1:0501. Other Syntek cameras - may be supported by the stk11xx driver, from which this is - derived, see - - This driver is deprecated and is scheduled for removal by - the end of 2022. See the TODO file for more information. - - To compile this driver as a module, choose M here: the - module will be called stkwebcam. - diff --git a/drivers/staging/media/stkwebcam/Makefile b/drivers/staging/media/stkwebcam/Makefile deleted file mode 100644 index 17ad7b6f43d0..000000000000 --- a/drivers/staging/media/stkwebcam/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -stkwebcam-objs := stk-webcam.o stk-sensor.o - -obj-$(CONFIG_VIDEO_STKWEBCAM) += stkwebcam.o - diff --git a/drivers/staging/media/stkwebcam/TODO b/drivers/staging/media/stkwebcam/TODO deleted file mode 100644 index 735304a72729..000000000000 --- a/drivers/staging/media/stkwebcam/TODO +++ /dev/null @@ -1,12 +0,0 @@ -This is a very old driver for very old hardware (specifically -laptops that use this sensor). In addition according to reports -the picture quality is quite bad. - -This is also one of the few drivers still not using the vb2 -framework (or even the old videobuf framework!), so this driver -is now deprecated with the intent of removing it altogether by -the end of 2022. - -In order to keep this driver it has to be converted to vb2. -If someone is interested in doing this work, then contact the -linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/stkwebcam/stk-sensor.c b/drivers/staging/media/stkwebcam/stk-sensor.c deleted file mode 100644 index 94aa6a27f934..000000000000 --- a/drivers/staging/media/stkwebcam/stk-sensor.c +++ /dev/null @@ -1,587 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) - * - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts derived from ov7670.c: - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet - * - * This file may be distributed under the terms of the GNU General - */ - -/* Controlling the sensor via the STK1125 vendor specific control interface: - * The camera uses an OmniVision sensor and the stk1125 provides an - * SCCB(i2c)-USB bridge which let us program the sensor. - * In my case the sensor id is 0x9652, it can be read from sensor's register - * 0x0A and 0x0B as follows: - * - read register #R: - * output #R to index 0x0208 - * output 0x0070 to index 0x0200 - * input 1 byte from index 0x0201 (some kind of status register) - * until its value is 0x01 - * input 1 byte from index 0x0209. This is the value of #R - * - write value V to register #R - * output #R to index 0x0204 - * output V to index 0x0205 - * output 0x0005 to index 0x0200 - * input 1 byte from index 0x0201 until its value becomes 0x04 - */ - -/* It seems the i2c bus is controlled with these registers */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stk-webcam.h" - -#define STK_IIC_BASE (0x0200) -# define STK_IIC_OP (STK_IIC_BASE) -# define STK_IIC_OP_TX (0x05) -# define STK_IIC_OP_RX (0x70) -# define STK_IIC_STAT (STK_IIC_BASE+1) -# define STK_IIC_STAT_TX_OK (0x04) -# define STK_IIC_STAT_RX_OK (0x01) -/* I don't know what does this register. - * when it is 0x00 or 0x01, we cannot talk to the sensor, - * other values work */ -# define STK_IIC_ENABLE (STK_IIC_BASE+2) -# define STK_IIC_ENABLE_NO (0x00) -/* This is what the driver writes in windows */ -# define STK_IIC_ENABLE_YES (0x1e) -/* - * Address of the slave. Seems like the binary driver look for the - * sensor in multiple places, attempting a reset sequence. - * We only know about the ov9650 - */ -# define STK_IIC_ADDR (STK_IIC_BASE+3) -# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) -# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) -# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) -# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) - -#define MAX_RETRIES (50) - -#define SENSOR_ADDRESS (0x60) - -/* From ov7670.c (These registers aren't fully accurate) */ - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define COM1_QFMT 0x20 /* QVGA/QCIF format */ -#define COM1_SKIP_0 0x00 /* Do not skip any row */ -#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ -#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clock control */ -#define CLK_PLL 0x80 /* Enable internal PLL */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_SXGA 0x00 -#define COM7_FMT_VGA 0x40 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ -#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGBFIXME 0x20 /* FIXME */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - - - -/* Returns 0 if OK */ -static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) -{ - int i = 0; - u8 tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_TX_OK) { - if (tmpval) - pr_err("stk_sensor_outb failed, status=0x%02x\n", - tmpval); - return 1; - } else - return 0; -} - -static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) -{ - int i = 0; - u8 tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_RX_OK) { - if (tmpval) - pr_err("stk_sensor_inb failed, status=0x%02x\n", - tmpval); - return 1; - } - - if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) - return 1; - - *val = tmpval; - return 0; -} - -static int stk_sensor_write_regvals(struct stk_camera *dev, - struct regval *rv) -{ - int ret; - if (rv == NULL) - return 0; - while (rv->reg != 0xff || rv->val != 0xff) { - ret = stk_sensor_outb(dev, rv->reg, rv->val); - if (ret != 0) - return ret; - rv++; - } - return 0; -} - -int stk_sensor_sleep(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); -} - -int stk_sensor_wakeup(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); -} - -static struct regval ov_initvals[] = { - {REG_CLKRC, CLK_PLL}, - {REG_COM11, 0x01}, - {0x6a, 0x7d}, - {REG_AECH, 0x40}, - {REG_GAIN, 0x00}, - {REG_BLUE, 0x80}, - {REG_RED, 0x80}, - /* Do not enable fast AEC for now */ - /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ - {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, - {0x39, 0x50}, {0x38, 0x93}, - {0x37, 0x00}, {0x35, 0x81}, - {REG_COM5, 0x20}, - {REG_COM1, 0x00}, - {REG_COM3, 0x00}, - {REG_COM4, 0x00}, - {REG_PSHFT, 0x00}, - {0x16, 0x07}, - {0x33, 0xe2}, {0x34, 0xbf}, - {REG_COM16, 0x00}, - {0x96, 0x04}, - /* Gamma curve values */ -/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, -*/ - {REG_GFIX, 0x40}, - {0x8e, 0x00}, - {REG_COM12, 0x73}, - {0x8f, 0xdf}, {0x8b, 0x06}, - {0x8c, 0x20}, - {0x94, 0x88}, {0x95, 0x88}, -/* {REG_COM15, 0xc1}, TODO */ - {0x29, 0x3f}, - {REG_COM6, 0x42}, - {REG_BD50MAX, 0x80}, - {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, - {REG_BD60MAX, 0x0a}, - {0x90, 0x00}, {0x91, 0x00}, - {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, - {REG_AEW, 0x68}, {REG_AEB, 0x5c}, - {REG_VPT, 0xc3}, - {REG_COM9, 0x2e}, - {0x2a, 0x00}, {0x2b, 0x00}, - - {0xff, 0xff}, /* END MARKER */ -}; - -/* Probe the I2C bus and initialise the sensor chip */ -int stk_sensor_init(struct stk_camera *dev) -{ - u8 idl = 0; - u8 idh = 0; - - if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) - || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) - || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { - pr_err("Sensor resetting failed\n"); - return -ENODEV; - } - msleep(10); - /* Read the manufacturer ID: ov = 0x7FA2 */ - if (stk_sensor_inb(dev, REG_MIDH, &idh) - || stk_sensor_inb(dev, REG_MIDL, &idl)) { - pr_err("Strange error reading sensor ID\n"); - return -ENODEV; - } - if (idh != 0x7f || idl != 0xa2) { - pr_err("Huh? you don't have a sensor from ovt\n"); - return -ENODEV; - } - if (stk_sensor_inb(dev, REG_PID, &idh) - || stk_sensor_inb(dev, REG_VER, &idl)) { - pr_err("Could not read sensor model\n"); - return -ENODEV; - } - stk_sensor_write_regvals(dev, ov_initvals); - msleep(10); - pr_info("OmniVision sensor detected, id %02X%02X at address %x\n", - idh, idl, SENSOR_ADDRESS); - return 0; -} - -/* V4L2_PIX_FMT_UYVY */ -static struct regval ov_fmt_uyvy[] = { - {REG_TSLB, TSLB_YLAST|0x08 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* V4L2_PIX_FMT_YUYV */ -static struct regval ov_fmt_yuyv[] = { - {REG_TSLB, 0 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; - -/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ -static struct regval ov_fmt_rgbr[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, 0x00}, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ -static struct regval ov_fmt_rgbp[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, TSLB_BYTEORD }, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_SRGGB8 */ -static struct regval ov_fmt_bayer[] = { - /* This changes color order */ - {REG_TSLB, 0x40}, /* BGGR */ - /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* - * Store a set of start/stop values into the camera. - */ -static int stk_sensor_set_hw(struct stk_camera *dev, - int hstart, int hstop, int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_HREF, v); -/* - * Vertical: similar arrangement (note: this is different from ov7670.c) - */ - ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_VREF, &v); - v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_VREF, v); - return ret; -} - - -int stk_sensor_configure(struct stk_camera *dev) -{ - int com7; - /* - * We setup the sensor to output dummy lines in low-res modes, - * so we don't get absurdly hight framerates. - */ - unsigned dummylines; - int flip; - struct regval *rv; - - switch (dev->vsettings.mode) { - case MODE_QCIF: com7 = COM7_FMT_QCIF; - dummylines = 604; - break; - case MODE_QVGA: com7 = COM7_FMT_QVGA; - dummylines = 267; - break; - case MODE_CIF: com7 = COM7_FMT_CIF; - dummylines = 412; - break; - case MODE_VGA: com7 = COM7_FMT_VGA; - dummylines = 11; - break; - case MODE_SXGA: com7 = COM7_FMT_SXGA; - dummylines = 0; - break; - default: - pr_err("Unsupported mode %d\n", dev->vsettings.mode); - return -EFAULT; - } - switch (dev->vsettings.palette) { - case V4L2_PIX_FMT_UYVY: - com7 |= COM7_YUV; - rv = ov_fmt_uyvy; - break; - case V4L2_PIX_FMT_YUYV: - com7 |= COM7_YUV; - rv = ov_fmt_yuyv; - break; - case V4L2_PIX_FMT_RGB565: - com7 |= COM7_RGB; - rv = ov_fmt_rgbp; - break; - case V4L2_PIX_FMT_RGB565X: - com7 |= COM7_RGB; - rv = ov_fmt_rgbr; - break; - case V4L2_PIX_FMT_SBGGR8: - com7 |= COM7_PBAYER; - rv = ov_fmt_bayer; - break; - default: - pr_err("Unsupported colorspace\n"); - return -EFAULT; - } - /*FIXME sometimes the sensor go to a bad state - stk_sensor_write_regvals(dev, ov_initvals); */ - stk_sensor_outb(dev, REG_COM7, com7); - msleep(50); - stk_sensor_write_regvals(dev, rv); - flip = (dev->vsettings.vflip?MVFP_FLIP:0) - | (dev->vsettings.hflip?MVFP_MIRROR:0); - stk_sensor_outb(dev, REG_MVFP, flip); - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 - && !dev->vsettings.vflip) - stk_sensor_outb(dev, REG_TSLB, 0x08); - stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); - stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); - msleep(50); - switch (dev->vsettings.mode) { - case MODE_VGA: - if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) - pr_err("stk_sensor_set_hw failed (VGA)\n"); - break; - case MODE_SXGA: - case MODE_CIF: - case MODE_QVGA: - case MODE_QCIF: - /*FIXME These settings seem ignored by the sensor - if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) - pr_err("stk_sensor_set_hw failed (SXGA)\n"); - */ - break; - } - msleep(10); - return 0; -} - -int stk_sensor_set_brightness(struct stk_camera *dev, int br) -{ - if (br < 0 || br > 0xff) - return -EINVAL; - stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); - stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); - return 0; -} - diff --git a/drivers/staging/media/stkwebcam/stk-webcam.c b/drivers/staging/media/stkwebcam/stk-webcam.c deleted file mode 100644 index 787edb3d47c2..000000000000 --- a/drivers/staging/media/stkwebcam/stk-webcam.c +++ /dev/null @@ -1,1434 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * stk-webcam.c : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts are inspired from cafe_ccic.c - * Copyright 2006-2007 Jonathan Corbet - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "stk-webcam.h" - - -static int hflip = -1; -module_param(hflip, int, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); - -static int vflip = -1; -module_param(vflip, int, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); -MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); - -/* Some cameras have audio interfaces, we aren't interested in those */ -static const struct usb_device_id stkwebcam_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, - { } -}; -MODULE_DEVICE_TABLE(usb, stkwebcam_table); - -/* - * The stk webcam laptop module is mounted upside down in some laptops :( - * - * Some background information (thanks to Hans de Goede for providing this): - * - * 1) Once upon a time the stkwebcam driver was written - * - * 2) The webcam in question was used mostly in Asus laptop models, including - * the laptop of the original author of the driver, and in these models, in - * typical Asus fashion (see the long long list for uvc cams inside v4l-utils), - * they mounted the webcam-module the wrong way up. So the hflip and vflip - * module options were given a default value of 1 (the correct value for - * upside down mounted models) - * - * 3) Years later I got a bug report from a user with a laptop with stkwebcam, - * where the module was actually mounted the right way up, and thus showed - * upside down under Linux. So now I was facing the choice of 2 options: - * - * a) Add a not-upside-down list to stkwebcam, which overrules the default. - * - * b) Do it like all the other drivers do, and make the default right for - * cams mounted the proper way and add an upside-down model list, with - * models where we need to flip-by-default. - * - * Despite knowing that going b) would cause a period of pain where we were - * building the table I opted to go for option b), since a) is just too ugly, - * and worse different from how every other driver does it leading to - * confusion in the long run. This change was made in kernel 3.6. - * - * So for any user report about upside-down images since kernel 3.6 ask them - * to provide the output of 'sudo dmidecode' so the laptop can be added in - * the table below. - */ -static const struct dmi_system_id stk_upside_down_dmi_table[] = { - { - .ident = "ASUS G1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "G1") - } - }, { - .ident = "ASUS F3JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "F3JC") - } - }, - { - .ident = "T12Rg-H", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"), - DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H") - } - }, - { - .ident = "ASUS A6VM", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") - } - }, - { - .ident = "ASUS A6JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") - } - }, - {} -}; - - -/* - * Basic stuff - */ -int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) - return ret; - else - return 0; -} - -int stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, - index, - &dev->read_reg_scratch, - sizeof(u8), - 500); - if (ret >= 0) - *value = dev->read_reg_scratch; - - if (ret < 0) - return ret; - else - return 0; -} - -static int stk_start_stream(struct stk_camera *dev) -{ - u8 value; - int i, ret; - u8 value_116, value_117; - - - if (!is_present(dev)) - return -ENODEV; - if (!is_memallocd(dev) || !is_initialised(dev)) { - pr_err("FIXME: Buffers are not allocated\n"); - return -EFAULT; - } - ret = usb_set_interface(dev->udev, 0, 5); - - if (ret < 0) - pr_err("usb_set_interface failed !\n"); - if (stk_sensor_wakeup(dev)) - pr_err("error awaking the sensor\n"); - - stk_camera_read_reg(dev, 0x0116, &value_116); - stk_camera_read_reg(dev, 0x0117, &value_117); - - stk_camera_write_reg(dev, 0x0116, 0x0000); - stk_camera_write_reg(dev, 0x0117, 0x0000); - - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value | 0x80); - - stk_camera_write_reg(dev, 0x0116, value_116); - stk_camera_write_reg(dev, 0x0117, value_117); - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) { - ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); - atomic_inc(&dev->urbs_used); - if (ret) - return ret; - } - } - set_streaming(dev); - return 0; -} - -static int stk_stop_stream(struct stk_camera *dev) -{ - u8 value; - int i; - if (is_present(dev)) { - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value & ~0x80); - if (dev->isobufs != NULL) { - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) - usb_kill_urb(dev->isobufs[i].urb); - } - } - unset_streaming(dev); - - if (usb_set_interface(dev->udev, 0, 0)) - pr_err("usb_set_interface failed !\n"); - if (stk_sensor_sleep(dev)) - pr_err("error suspending the sensor\n"); - } - return 0; -} - -/* - * This seems to be the shortest init sequence we - * must do in order to find the sensor - * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor - * is also reset. Maybe powers down it? - * Rest of values don't make a difference - */ - -static struct regval stk1125_initvals[] = { - /*TODO: What means this sequence? */ - {0x0000, 0x24}, - {0x0100, 0x21}, - {0x0002, 0x68}, - {0x0003, 0x80}, - {0x0005, 0x00}, - {0x0007, 0x03}, - {0x000d, 0x00}, - {0x000f, 0x02}, - {0x0300, 0x12}, - {0x0350, 0x41}, - {0x0351, 0x00}, - {0x0352, 0x00}, - {0x0353, 0x00}, - {0x0018, 0x10}, - {0x0019, 0x00}, - {0x001b, 0x0e}, - {0x001c, 0x46}, - {0x0300, 0x80}, - {0x001a, 0x04}, - {0x0110, 0x00}, - {0x0111, 0x00}, - {0x0112, 0x00}, - {0x0113, 0x00}, - - {0xffff, 0xff}, -}; - - -static int stk_initialise(struct stk_camera *dev) -{ - struct regval *rv; - int ret; - if (!is_present(dev)) - return -ENODEV; - if (is_initialised(dev)) - return 0; - rv = stk1125_initvals; - while (rv->reg != 0xffff) { - ret = stk_camera_write_reg(dev, rv->reg, rv->val); - if (ret) - return ret; - rv++; - } - if (stk_sensor_init(dev) == 0) { - set_initialised(dev); - return 0; - } else - return -1; -} - -/* *********************************************** */ -/* - * This function is called as an URB transfert is complete (Isochronous pipe). - * So, the traitement is done in interrupt time, so it has be fast, not crash, - * and not stall. Neat. - */ -static void stk_isoc_handler(struct urb *urb) -{ - int i; - int ret; - int framelen; - unsigned long flags; - - unsigned char *fill = NULL; - unsigned char *iso_buf = NULL; - - struct stk_camera *dev; - struct stk_sio_buffer *fb; - - dev = (struct stk_camera *) urb->context; - - if (dev == NULL) { - pr_err("isoc_handler called with NULL device !\n"); - return; - } - - if (urb->status == -ENOENT || urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN) { - atomic_dec(&dev->urbs_used); - return; - } - - spin_lock_irqsave(&dev->spinlock, flags); - - if (urb->status != -EINPROGRESS && urb->status != 0) { - pr_err("isoc_handler: urb->status == %d\n", urb->status); - goto resubmit; - } - - if (list_empty(&dev->sio_avail)) { - /*FIXME Stop streaming after a while */ - pr_err_ratelimited("isoc_handler without available buffer!\n"); - goto resubmit; - } - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fill = fb->buffer + fb->v4lbuf.bytesused; - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status != 0) { - if (urb->iso_frame_desc[i].status != -EXDEV) - pr_err("Frame %d has error %d\n", - i, urb->iso_frame_desc[i].status); - continue; - } - framelen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (framelen <= 4) - continue; /* no data */ - - /* - * we found something informational from there - * the isoc frames have to type of headers - * type1: 00 xx 00 00 or 20 xx 00 00 - * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 - * xx is a sequencer which has never been seen over 0x3f - * imho data written down looks like bayer, i see similarities - * after every 640 bytes - */ - if (*iso_buf & 0x80) { - framelen -= 8; - iso_buf += 8; - /* This marks a new frame */ - if (fb->v4lbuf.bytesused != 0 - && fb->v4lbuf.bytesused != dev->frame_size) { - pr_err_ratelimited("frame %d, bytesused=%d, skipping\n", - i, fb->v4lbuf.bytesused); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else if (fb->v4lbuf.bytesused == dev->frame_size) { - if (list_is_singular(&dev->sio_avail)) { - /* Always reuse the last buffer */ - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else { - list_move_tail(dev->sio_avail.next, - &dev->sio_full); - wake_up(&dev->wait_frame); - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } - } - } else { - framelen -= 4; - iso_buf += 4; - } - - /* Our buffer is full !!! */ - if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { - pr_err_ratelimited("Frame buffer overflow, lost sync\n"); - /*FIXME Do something here? */ - continue; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - memcpy(fill, iso_buf, framelen); - spin_lock_irqsave(&dev->spinlock, flags); - fill += framelen; - - /* New size of our buffer */ - fb->v4lbuf.bytesused += framelen; - } - -resubmit: - spin_unlock_irqrestore(&dev->spinlock, flags); - urb->dev = dev->udev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret != 0) { - pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n", - ret); - } -} - -/* -------------------------------------------- */ - -static int stk_prepare_iso(struct stk_camera *dev) -{ - void *kbuf; - int i, j; - struct urb *urb; - struct usb_device *udev; - - if (dev == NULL) - return -ENXIO; - udev = dev->udev; - - if (dev->isobufs) - pr_err("isobufs already allocated. Bad\n"); - else - dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), - GFP_KERNEL); - if (dev->isobufs == NULL) { - pr_err("Unable to allocate iso buffers\n"); - return -ENOMEM; - } - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].data == NULL) { - kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); - if (kbuf == NULL) { - pr_err("Failed to allocate iso buffer %d\n", i); - goto isobufs_out; - } - dev->isobufs[i].data = kbuf; - } else - pr_err("isobuf data already allocated\n"); - if (dev->isobufs[i].urb == NULL) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) - goto isobufs_out; - dev->isobufs[i].urb = urb; - } else { - pr_err("Killing URB\n"); - usb_kill_urb(dev->isobufs[i].urb); - urb = dev->isobufs[i].urb; - } - urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->isobufs[i].data; - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = stk_isoc_handler; - urb->context = dev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; - } - } - set_memallocd(dev); - return 0; - -isobufs_out: - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) - kfree(dev->isobufs[i].data); - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) - usb_free_urb(dev->isobufs[i].urb); - kfree(dev->isobufs); - dev->isobufs = NULL; - return -ENOMEM; -} - -static void stk_clean_iso(struct stk_camera *dev) -{ - int i; - - if (dev == NULL || dev->isobufs == NULL) - return; - - for (i = 0; i < MAX_ISO_BUFS; i++) { - struct urb *urb; - - urb = dev->isobufs[i].urb; - if (urb) { - if (atomic_read(&dev->urbs_used) && is_present(dev)) - usb_kill_urb(urb); - usb_free_urb(urb); - } - kfree(dev->isobufs[i].data); - } - kfree(dev->isobufs); - dev->isobufs = NULL; - unset_memallocd(dev); -} - -static int stk_setup_siobuf(struct stk_camera *dev, int index) -{ - struct stk_sio_buffer *buf = dev->sio_bufs + index; - INIT_LIST_HEAD(&buf->list); - buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); - buf->buffer = vmalloc_user(buf->v4lbuf.length); - if (buf->buffer == NULL) - return -ENOMEM; - buf->mapcount = 0; - buf->dev = dev; - buf->v4lbuf.index = index; - buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - buf->v4lbuf.field = V4L2_FIELD_NONE; - buf->v4lbuf.memory = V4L2_MEMORY_MMAP; - buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; - return 0; -} - -static int stk_free_sio_buffers(struct stk_camera *dev) -{ - int i; - int nbufs; - unsigned long flags; - if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) - return 0; - /* - * If any buffers are mapped, we cannot free them at all. - */ - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].mapcount > 0) - return -EBUSY; - } - /* - * OK, let's do it. - */ - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - nbufs = dev->n_sbufs; - dev->n_sbufs = 0; - spin_unlock_irqrestore(&dev->spinlock, flags); - for (i = 0; i < nbufs; i++) - vfree(dev->sio_bufs[i].buffer); - kfree(dev->sio_bufs); - dev->sio_bufs = NULL; - return 0; -} - -static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int i; - if (dev->sio_bufs != NULL) - pr_err("sio_bufs already allocated\n"); - else { - dev->sio_bufs = kcalloc(n_sbufs, - sizeof(struct stk_sio_buffer), - GFP_KERNEL); - if (dev->sio_bufs == NULL) - return -ENOMEM; - for (i = 0; i < n_sbufs; i++) { - if (stk_setup_siobuf(dev, i)) - return (dev->n_sbufs > 1 ? 0 : -ENOMEM); - dev->n_sbufs = i+1; - } - } - return 0; -} - -static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int err; - err = stk_prepare_iso(dev); - if (err) { - stk_clean_iso(dev); - return err; - } - err = stk_prepare_sio_buffers(dev, n_sbufs); - if (err) { - stk_free_sio_buffers(dev); - return err; - } - return 0; -} - -static void stk_free_buffers(struct stk_camera *dev) -{ - stk_clean_iso(dev); - stk_free_sio_buffers(dev); -} -/* -------------------------------------------- */ - -/* v4l file operations */ - -static int v4l_stk_open(struct file *fp) -{ - struct stk_camera *dev = video_drvdata(fp); - int err; - - if (dev == NULL || !is_present(dev)) - return -ENXIO; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (!dev->first_init) - stk_camera_write_reg(dev, 0x0, 0x24); - else - dev->first_init = 0; - - err = v4l2_fh_open(fp); - if (!err) - usb_autopm_get_interface(dev->interface); - mutex_unlock(&dev->lock); - return err; -} - -static int v4l_stk_release(struct file *fp) -{ - struct stk_camera *dev = video_drvdata(fp); - - mutex_lock(&dev->lock); - if (dev->owner == fp) { - stk_stop_stream(dev); - stk_free_buffers(dev); - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - } - - usb_autopm_put_interface(dev->interface); - mutex_unlock(&dev->lock); - return v4l2_fh_release(fp); -} - -static ssize_t stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - int i; - int ret; - unsigned long flags; - struct stk_sio_buffer *sbuf; - struct stk_camera *dev = video_drvdata(fp); - - if (!is_present(dev)) - return -EIO; - if (dev->owner && (!dev->reading || dev->owner != fp)) - return -EBUSY; - dev->owner = fp; - if (!is_streaming(dev)) { - if (stk_initialise(dev) - || stk_allocate_buffers(dev, 3) - || stk_start_stream(dev)) - return -ENOMEM; - dev->reading = 1; - spin_lock_irqsave(&dev->spinlock, flags); - for (i = 0; i < dev->n_sbufs; i++) { - list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); - dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - } - if (*f_pos == 0) { - if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - } - if (count + *f_pos > dev->frame_size) - count = dev->frame_size - *f_pos; - spin_lock_irqsave(&dev->spinlock, flags); - if (list_empty(&dev->sio_full)) { - spin_unlock_irqrestore(&dev->spinlock, flags); - pr_err("BUG: No siobufs ready\n"); - return 0; - } - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - spin_unlock_irqrestore(&dev->spinlock, flags); - - if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) - return -EFAULT; - - *f_pos += count; - - if (*f_pos >= dev->frame_size) { - *f_pos = 0; - spin_lock_irqsave(&dev->spinlock, flags); - list_move_tail(&sbuf->list, &dev->sio_avail); - spin_unlock_irqrestore(&dev->spinlock, flags); - } - return count; -} - -static ssize_t v4l_stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - struct stk_camera *dev = video_drvdata(fp); - int ret; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = stk_read(fp, buf, count, f_pos); - mutex_unlock(&dev->lock); - return ret; -} - -static __poll_t v4l_stk_poll(struct file *fp, poll_table *wait) -{ - struct stk_camera *dev = video_drvdata(fp); - __poll_t res = v4l2_ctrl_poll(fp, wait); - - poll_wait(fp, &dev->wait_frame, wait); - - if (!is_present(dev)) - return EPOLLERR; - - if (!list_empty(&dev->sio_full)) - return res | EPOLLIN | EPOLLRDNORM; - - return res; -} - - -static void stk_v4l_vm_open(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount++; -} -static void stk_v4l_vm_close(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount--; - if (sbuf->mapcount == 0) - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} -static const struct vm_operations_struct stk_v4l_vm_ops = { - .open = stk_v4l_vm_open, - .close = stk_v4l_vm_close -}; - -static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) -{ - unsigned int i; - int ret; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct stk_camera *dev = video_drvdata(fp); - struct stk_sio_buffer *sbuf = NULL; - - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { - sbuf = dev->sio_bufs + i; - break; - } - } - if (sbuf == NULL) - return -EINVAL; - ret = remap_vmalloc_range(vma, sbuf->buffer, 0); - if (ret) - return ret; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = sbuf; - vma->vm_ops = &stk_v4l_vm_ops; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; - stk_v4l_vm_open(vma); - return 0; -} - -/* v4l ioctl handlers */ - -static int stk_vidioc_querycap(struct file *filp, - void *priv, struct v4l2_capability *cap) -{ - struct stk_camera *dev = video_drvdata(filp); - - strscpy(cap->driver, "stk", sizeof(cap->driver)); - strscpy(cap->card, "stk", sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - return 0; -} - -static int stk_vidioc_enum_input(struct file *filp, - void *priv, struct v4l2_input *input) -{ - if (input->index != 0) - return -EINVAL; - - strscpy(input->name, "Syntek USB Camera", sizeof(input->name)); - input->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - - -static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int stk_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct stk_camera *dev = - container_of(ctrl->handler, struct stk_camera, hdl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return stk_sensor_set_brightness(dev, ctrl->val); - case V4L2_CID_HFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.hflip = !ctrl->val; - else - dev->vsettings.hflip = ctrl->val; - return 0; - case V4L2_CID_VFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.vflip = !ctrl->val; - else - dev->vsettings.vflip = ctrl->val; - return 0; - default: - return -EINVAL; - } - return 0; -} - - -static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_fmtdesc *fmtd) -{ - switch (fmtd->index) { - case 0: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565; - break; - case 1: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; - break; - case 2: - fmtd->pixelformat = V4L2_PIX_FMT_UYVY; - break; - case 3: - fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; - break; - case 4: - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - break; - default: - return -EINVAL; - } - return 0; -} - -static struct stk_size { - unsigned w; - unsigned h; - enum stk_mode m; -} stk_sizes[] = { - { .w = 1280, .h = 1024, .m = MODE_SXGA, }, - { .w = 640, .h = 480, .m = MODE_VGA, }, - { .w = 352, .h = 288, .m = MODE_CIF, }, - { .w = 320, .h = 240, .m = MODE_QVGA, }, - { .w = 176, .h = 144, .m = MODE_QCIF, }, -}; - -static int stk_vidioc_g_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix_format = &f->fmt.pix; - struct stk_camera *dev = video_drvdata(filp); - int i; - - for (i = 0; i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode; i++) - ; - if (i == ARRAY_SIZE(stk_sizes)) { - pr_err("ERROR: mode invalid\n"); - return -EINVAL; - } - pix_format->width = stk_sizes[i].w; - pix_format->height = stk_sizes[i].h; - pix_format->field = V4L2_FIELD_NONE; - pix_format->colorspace = V4L2_COLORSPACE_SRGB; - pix_format->pixelformat = dev->vsettings.palette; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - pix_format->bytesperline = pix_format->width; - else - pix_format->bytesperline = 2 * pix_format->width; - pix_format->sizeimage = pix_format->bytesperline - * pix_format->height; - return 0; -} - -static int stk_try_fmt_vid_cap(struct file *filp, - struct v4l2_format *fmtd, int *idx) -{ - int i; - switch (fmtd->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - break; - default: - return -EINVAL; - } - for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { - if (fmtd->fmt.pix.width > stk_sizes[i].w) - break; - } - if (i == ARRAY_SIZE(stk_sizes) - || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) - < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { - fmtd->fmt.pix.height = stk_sizes[i-1].h; - fmtd->fmt.pix.width = stk_sizes[i-1].w; - if (idx) - *idx = i - 1; - } else { - fmtd->fmt.pix.height = stk_sizes[i].h; - fmtd->fmt.pix.width = stk_sizes[i].w; - if (idx) - *idx = i; - } - - fmtd->fmt.pix.field = V4L2_FIELD_NONE; - fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) - fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; - else - fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; - fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline - * fmtd->fmt.pix.height; - return 0; -} - -static int stk_vidioc_try_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - return stk_try_fmt_vid_cap(filp, fmtd, NULL); -} - -static int stk_setup_format(struct stk_camera *dev) -{ - int i = 0; - int depth; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - depth = 1; - else - depth = 2; - while (i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode) - i++; - if (i == ARRAY_SIZE(stk_sizes)) { - pr_err("Something is broken in %s\n", __func__); - return -EFAULT; - } - /* This registers controls some timings, not sure of what. */ - stk_camera_write_reg(dev, 0x001b, 0x0e); - if (dev->vsettings.mode == MODE_SXGA) - stk_camera_write_reg(dev, 0x001c, 0x0e); - else - stk_camera_write_reg(dev, 0x001c, 0x46); - /* - * Registers 0x0115 0x0114 are the size of each line (bytes), - * regs 0x0117 0x0116 are the height of the image. - */ - stk_camera_write_reg(dev, 0x0115, - ((stk_sizes[i].w * depth) >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0114, - (stk_sizes[i].w * depth) & 0xff); - stk_camera_write_reg(dev, 0x0117, - (stk_sizes[i].h >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0116, - stk_sizes[i].h & 0xff); - return stk_sensor_configure(dev); -} - -static int stk_vidioc_s_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int ret; - int idx; - struct stk_camera *dev = video_drvdata(filp); - - if (dev == NULL) - return -ENODEV; - if (!is_present(dev)) - return -ENODEV; - if (is_streaming(dev)) - return -EBUSY; - if (dev->owner) - return -EBUSY; - ret = stk_try_fmt_vid_cap(filp, fmtd, &idx); - if (ret) - return ret; - - dev->vsettings.palette = fmtd->fmt.pix.pixelformat; - stk_free_buffers(dev); - dev->frame_size = fmtd->fmt.pix.sizeimage; - dev->vsettings.mode = stk_sizes[idx].m; - - stk_initialise(dev); - return stk_setup_format(dev); -} - -static int stk_vidioc_reqbufs(struct file *filp, - void *priv, struct v4l2_requestbuffers *rb) -{ - struct stk_camera *dev = video_drvdata(filp); - - if (dev == NULL) - return -ENODEV; - if (rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (is_streaming(dev) - || (dev->owner && dev->owner != filp)) - return -EBUSY; - stk_free_buffers(dev); - if (rb->count == 0) { - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - return 0; - } - dev->owner = filp; - - /*FIXME If they ask for zero, we must stop streaming and free */ - if (rb->count < 3) - rb->count = 3; - /* Arbitrary limit */ - else if (rb->count > 5) - rb->count = 5; - - stk_allocate_buffers(dev, rb->count); - rb->count = dev->n_sbufs; - return 0; -} - -static int stk_vidioc_querybuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_qbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - unsigned long flags; - - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) - return 0; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; - spin_lock_irqsave(&dev->spinlock, flags); - list_add_tail(&sbuf->list, &dev->sio_avail); - *buf = sbuf->v4lbuf; - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - -static int stk_vidioc_dqbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - unsigned long flags; - int ret; - - if (!is_streaming(dev)) - return -EINVAL; - - if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - - spin_lock_irqsave(&dev->spinlock, flags); - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - list_del_init(&sbuf->list); - spin_unlock_irqrestore(&dev->spinlock, flags); - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; - sbuf->v4lbuf.sequence = ++dev->sequence; - v4l2_buffer_set_timestamp(&sbuf->v4lbuf, ktime_get_ns()); - - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_streamon(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = video_drvdata(filp); - if (is_streaming(dev)) - return 0; - if (dev->sio_bufs == NULL) - return -EINVAL; - dev->sequence = 0; - return stk_start_stream(dev); -} - -static int stk_vidioc_streamoff(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = video_drvdata(filp); - unsigned long flags; - int i; - stk_stop_stream(dev); - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - for (i = 0; i < dev->n_sbufs; i++) { - INIT_LIST_HEAD(&dev->sio_bufs[i].list); - dev->sio_bufs[i].v4lbuf.flags = 0; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - - -static int stk_vidioc_g_parm(struct file *filp, - void *priv, struct v4l2_streamparm *sp) -{ - /*FIXME This is not correct */ - sp->parm.capture.timeperframe.numerator = 1; - sp->parm.capture.timeperframe.denominator = 30; - sp->parm.capture.readbuffers = 2; - return 0; -} - -static int stk_vidioc_enum_framesizes(struct file *filp, - void *priv, struct v4l2_frmsizeenum *frms) -{ - if (frms->index >= ARRAY_SIZE(stk_sizes)) - return -EINVAL; - switch (frms->pixel_format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; - frms->discrete.width = stk_sizes[frms->index].w; - frms->discrete.height = stk_sizes[frms->index].h; - return 0; - default: return -EINVAL; - } -} - -static const struct v4l2_ctrl_ops stk_ctrl_ops = { - .s_ctrl = stk_s_ctrl, -}; - -static const struct v4l2_file_operations v4l_stk_fops = { - .owner = THIS_MODULE, - .open = v4l_stk_open, - .release = v4l_stk_release, - .read = v4l_stk_read, - .poll = v4l_stk_poll, - .mmap = v4l_stk_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { - .vidioc_querycap = stk_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = stk_vidioc_enum_input, - .vidioc_s_input = stk_vidioc_s_input, - .vidioc_g_input = stk_vidioc_g_input, - .vidioc_reqbufs = stk_vidioc_reqbufs, - .vidioc_querybuf = stk_vidioc_querybuf, - .vidioc_qbuf = stk_vidioc_qbuf, - .vidioc_dqbuf = stk_vidioc_dqbuf, - .vidioc_streamon = stk_vidioc_streamon, - .vidioc_streamoff = stk_vidioc_streamoff, - .vidioc_g_parm = stk_vidioc_g_parm, - .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static void stk_v4l_dev_release(struct video_device *vd) -{ - struct stk_camera *dev = vdev_to_camera(vd); - - if (dev->sio_bufs != NULL || dev->isobufs != NULL) - pr_err("We are leaking memory\n"); - usb_put_intf(dev->interface); - usb_put_dev(dev->udev); - - v4l2_ctrl_handler_free(&dev->hdl); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); -} - -static const struct video_device stk_v4l_data = { - .name = "stkwebcam", - .fops = &v4l_stk_fops, - .ioctl_ops = &v4l_stk_ioctl_ops, - .release = stk_v4l_dev_release, -}; - - -static int stk_register_video_device(struct stk_camera *dev) -{ - int err; - - dev->vdev = stk_v4l_data; - dev->vdev.lock = &dev->lock; - dev->vdev.v4l2_dev = &dev->v4l2_dev; - dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - video_set_drvdata(&dev->vdev, dev); - err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); - if (err) - pr_err("v4l registration failed\n"); - else - pr_info("Syntek USB2.0 Camera is now controlling device %s\n", - video_device_node_name(&dev->vdev)); - return err; -} - - -/* USB Stuff */ - -static int stk_camera_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct v4l2_ctrl_handler *hdl; - int err = 0; - int i; - - struct stk_camera *dev = NULL; - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); - if (dev == NULL) { - pr_err("Out of memory !\n"); - return -ENOMEM; - } - err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (err < 0) { - dev_err(&udev->dev, "couldn't register v4l2_device\n"); - kfree(dev); - return err; - } - hdl = &dev->hdl; - v4l2_ctrl_handler_init(hdl, 3); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 0xff, 0x1, 0x60); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 1); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 1); - if (hdl->error) { - err = hdl->error; - dev_err(&udev->dev, "couldn't register control\n"); - goto error; - } - dev->v4l2_dev.ctrl_handler = hdl; - - spin_lock_init(&dev->spinlock); - mutex_init(&dev->lock); - init_waitqueue_head(&dev->wait_frame); - dev->first_init = 1; /* webcam LED management */ - - dev->udev = usb_get_dev(udev); - dev->interface = interface; - usb_get_intf(interface); - - if (hflip != -1) - dev->vsettings.hflip = hflip; - else if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.hflip = 1; - else - dev->vsettings.hflip = 0; - if (vflip != -1) - dev->vsettings.vflip = vflip; - else if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.vflip = 1; - else - dev->vsettings.vflip = 0; - dev->n_sbufs = 0; - set_present(dev); - - /* Set up the endpoint information - * use only the first isoc-in endpoint - * for the current alternate setting */ - iface_desc = interface->cur_altsetting; - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->isoc_ep - && usb_endpoint_is_isoc_in(endpoint)) { - /* we found an isoc in endpoint */ - dev->isoc_ep = usb_endpoint_num(endpoint); - break; - } - } - if (!dev->isoc_ep) { - pr_err("Could not find isoc-in endpoint\n"); - err = -ENODEV; - goto error_put; - } - dev->vsettings.palette = V4L2_PIX_FMT_RGB565; - dev->vsettings.mode = MODE_VGA; - dev->frame_size = 640 * 480 * 2; - - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - - usb_set_intfdata(interface, dev); - - err = stk_register_video_device(dev); - if (err) - goto error_put; - - return 0; - -error_put: - usb_put_intf(interface); - usb_put_dev(dev->udev); -error: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - return err; -} - -static void stk_camera_disconnect(struct usb_interface *interface) -{ - struct stk_camera *dev = usb_get_intfdata(interface); - - usb_set_intfdata(interface, NULL); - unset_present(dev); - - wake_up_interruptible(&dev->wait_frame); - - pr_info("Syntek USB2.0 Camera release resources device %s\n", - video_device_node_name(&dev->vdev)); - - video_unregister_device(&dev->vdev); -} - -#ifdef CONFIG_PM -static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (is_streaming(dev)) { - stk_stop_stream(dev); - /* yes, this is ugly */ - set_streaming(dev); - } - return 0; -} - -static int stk_camera_resume(struct usb_interface *intf) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (!is_initialised(dev)) - return 0; - unset_initialised(dev); - stk_initialise(dev); - stk_camera_write_reg(dev, 0x0, 0x49); - stk_setup_format(dev); - if (is_streaming(dev)) - stk_start_stream(dev); - return 0; -} -#endif - -static struct usb_driver stk_camera_driver = { - .name = "stkwebcam", - .probe = stk_camera_probe, - .disconnect = stk_camera_disconnect, - .id_table = stkwebcam_table, -#ifdef CONFIG_PM - .suspend = stk_camera_suspend, - .resume = stk_camera_resume, -#endif -}; - -module_usb_driver(stk_camera_driver); diff --git a/drivers/staging/media/stkwebcam/stk-webcam.h b/drivers/staging/media/stkwebcam/stk-webcam.h deleted file mode 100644 index 136decffe9ce..000000000000 --- a/drivers/staging/media/stkwebcam/stk-webcam.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * stk-webcam.h : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - */ - -#ifndef STKWEBCAM_H -#define STKWEBCAM_H - -#include -#include -#include -#include - -#define DRIVER_VERSION "v0.0.1" -#define DRIVER_VERSION_NUM 0x000001 - -#define MAX_ISO_BUFS 3 -#define ISO_FRAMES_PER_DESC 16 -#define ISO_MAX_FRAME_SIZE 3 * 1024 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - -struct stk_iso_buf { - void *data; - int length; - int read; - struct urb *urb; -}; - -/* Streaming IO buffers */ -struct stk_sio_buffer { - struct v4l2_buffer v4lbuf; - char *buffer; - int mapcount; - struct stk_camera *dev; - struct list_head list; -}; - -enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; - -struct stk_video { - enum stk_mode mode; - __u32 palette; - int hflip; - int vflip; -}; - -enum stk_status { - S_PRESENT = 1, - S_INITIALISED = 2, - S_MEMALLOCD = 4, - S_STREAMING = 8, -}; -#define is_present(dev) ((dev)->status & S_PRESENT) -#define is_initialised(dev) ((dev)->status & S_INITIALISED) -#define is_streaming(dev) ((dev)->status & S_STREAMING) -#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) -#define set_present(dev) ((dev)->status = S_PRESENT) -#define unset_present(dev) ((dev)->status &= \ - ~(S_PRESENT|S_INITIALISED|S_STREAMING)) -#define set_initialised(dev) ((dev)->status |= S_INITIALISED) -#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) -#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) -#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) -#define set_streaming(dev) ((dev)->status |= S_STREAMING) -#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) - -struct regval { - unsigned reg; - unsigned val; -}; - -struct stk_camera { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct video_device vdev; - struct usb_device *udev; - struct usb_interface *interface; - int webcam_model; - struct file *owner; - struct mutex lock; - int first_init; - - u8 isoc_ep; - - /* Not sure if this is right */ - atomic_t urbs_used; - - struct stk_video vsettings; - - enum stk_status status; - - spinlock_t spinlock; - wait_queue_head_t wait_frame; - - struct stk_iso_buf *isobufs; - - int frame_size; - /* Streaming buffers */ - int reading; - unsigned int n_sbufs; - struct stk_sio_buffer *sio_bufs; - struct list_head sio_avail; - struct list_head sio_full; - unsigned sequence; - - u8 read_reg_scratch; -}; - -#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) - -int stk_camera_write_reg(struct stk_camera *, u16, u8); -int stk_camera_read_reg(struct stk_camera *, u16, u8 *); - -int stk_sensor_init(struct stk_camera *); -int stk_sensor_configure(struct stk_camera *); -int stk_sensor_sleep(struct stk_camera *dev); -int stk_sensor_wakeup(struct stk_camera *dev); -int stk_sensor_set_brightness(struct stk_camera *dev, int br); - -#endif -- cgit v1.3-14-g43fede From be8cebc46d9d38166a1b3fda22a018ae52b0928e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:42 +0200 Subject: media: cpia2: deprecate this driver Deprecate the cpia2 driver. This driver does not use the vb2 framework for video streaming, instead it implements its own version. We want to get rid of these old drivers, so deprecated it for future removal. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/Kconfig | 1 - drivers/media/usb/Makefile | 1 - drivers/media/usb/cpia2/Kconfig | 10 - drivers/media/usb/cpia2/Makefile | 4 - drivers/media/usb/cpia2/cpia2.h | 475 ---- drivers/media/usb/cpia2/cpia2_core.c | 2434 -------------------- drivers/media/usb/cpia2/cpia2_registers.h | 463 ---- drivers/media/usb/cpia2/cpia2_usb.c | 966 -------- drivers/media/usb/cpia2/cpia2_v4l.c | 1226 ---------- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/deprecated/cpia2/Kconfig | 13 + drivers/staging/media/deprecated/cpia2/Makefile | 4 + drivers/staging/media/deprecated/cpia2/TODO | 6 + drivers/staging/media/deprecated/cpia2/cpia2.h | 475 ++++ .../staging/media/deprecated/cpia2/cpia2_core.c | 2434 ++++++++++++++++++++ .../media/deprecated/cpia2/cpia2_registers.h | 463 ++++ drivers/staging/media/deprecated/cpia2/cpia2_usb.c | 966 ++++++++ drivers/staging/media/deprecated/cpia2/cpia2_v4l.c | 1226 ++++++++++ 19 files changed, 5589 insertions(+), 5580 deletions(-) delete mode 100644 drivers/media/usb/cpia2/Kconfig delete mode 100644 drivers/media/usb/cpia2/Makefile delete mode 100644 drivers/media/usb/cpia2/cpia2.h delete mode 100644 drivers/media/usb/cpia2/cpia2_core.c delete mode 100644 drivers/media/usb/cpia2/cpia2_registers.h delete mode 100644 drivers/media/usb/cpia2/cpia2_usb.c delete mode 100644 drivers/media/usb/cpia2/cpia2_v4l.c create mode 100644 drivers/staging/media/deprecated/cpia2/Kconfig create mode 100644 drivers/staging/media/deprecated/cpia2/Makefile create mode 100644 drivers/staging/media/deprecated/cpia2/TODO create mode 100644 drivers/staging/media/deprecated/cpia2/cpia2.h create mode 100644 drivers/staging/media/deprecated/cpia2/cpia2_core.c create mode 100644 drivers/staging/media/deprecated/cpia2/cpia2_registers.h create mode 100644 drivers/staging/media/deprecated/cpia2/cpia2_usb.c create mode 100644 drivers/staging/media/deprecated/cpia2/cpia2_v4l.c diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index af88e0766388..afbb8dd28b5b 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -13,7 +13,6 @@ if MEDIA_USB_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Webcam devices" -source "drivers/media/usb/cpia2/Kconfig" source "drivers/media/usb/gspca/Kconfig" source "drivers/media/usb/pwc/Kconfig" source "drivers/media/usb/s2255/Kconfig" diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 25fa2015b179..fa8e16ff9b03 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -24,7 +24,6 @@ obj-$(CONFIG_USB_MSI2500) += msi2500/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ -obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ diff --git a/drivers/media/usb/cpia2/Kconfig b/drivers/media/usb/cpia2/Kconfig deleted file mode 100644 index da2c6862b4a2..000000000000 --- a/drivers/media/usb/cpia2/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_CPIA2 - tristate "CPiA2 Video For Linux" - depends on USB && VIDEO_DEV - help - This is the video4linux driver for cameras based on Vision's CPiA2 - (Colour Processor Interface ASIC), such as the Digital Blue QX5 - Microscope. If you have one of these cameras, say Y here - - This driver is also available as a module (cpia2). diff --git a/drivers/media/usb/cpia2/Makefile b/drivers/media/usb/cpia2/Makefile deleted file mode 100644 index 05664141f4d7..000000000000 --- a/drivers/media/usb/cpia2/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o - -obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o diff --git a/drivers/media/usb/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h deleted file mode 100644 index 57b7f1ea68da..000000000000 --- a/drivers/media/usb/cpia2/cpia2.h +++ /dev/null @@ -1,475 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/**************************************************************************** - * - * Filename: cpia2.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPiA2 based video cameras. - * - * This driver is modelled on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - ****************************************************************************/ - -#ifndef __CPIA2_H__ -#define __CPIA2_H__ - -#include -#include -#include -#include -#include -#include - -#include "cpia2_registers.h" - -/* define for verbose debug output */ -//#define _CPIA2_DEBUG_ - -/*** - * Image defines - ***/ - -/* Misc constants */ -#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */ - -/* USB Transfer mode */ -#define XFER_ISOC 0 -#define XFER_BULK 1 - -/* USB Alternates */ -#define USBIF_CMDONLY 0 -#define USBIF_BULK 1 -#define USBIF_ISO_1 2 /* 128 bytes/ms */ -#define USBIF_ISO_2 3 /* 384 bytes/ms */ -#define USBIF_ISO_3 4 /* 640 bytes/ms */ -#define USBIF_ISO_4 5 /* 768 bytes/ms */ -#define USBIF_ISO_5 6 /* 896 bytes/ms */ -#define USBIF_ISO_6 7 /* 1023 bytes/ms */ - -/* Flicker Modes */ -#define NEVER_FLICKER 0 -#define FLICKER_60 60 -#define FLICKER_50 50 - -/* Debug flags */ -#define DEBUG_NONE 0 -#define DEBUG_REG 0x00000001 -#define DEBUG_DUMP_PATCH 0x00000002 -#define DEBUG_DUMP_REGS 0x00000004 - -/*** - * Video frame sizes - ***/ -enum { - VIDEOSIZE_VGA = 0, /* 640x480 */ - VIDEOSIZE_CIF, /* 352x288 */ - VIDEOSIZE_QVGA, /* 320x240 */ - VIDEOSIZE_QCIF, /* 176x144 */ - VIDEOSIZE_288_216, - VIDEOSIZE_256_192, - VIDEOSIZE_224_168, - VIDEOSIZE_192_144, -}; - -#define STV_IMAGE_CIF_ROWS 288 -#define STV_IMAGE_CIF_COLS 352 - -#define STV_IMAGE_QCIF_ROWS 144 -#define STV_IMAGE_QCIF_COLS 176 - -#define STV_IMAGE_VGA_ROWS 480 -#define STV_IMAGE_VGA_COLS 640 - -#define STV_IMAGE_QVGA_ROWS 240 -#define STV_IMAGE_QVGA_COLS 320 - -#define JPEG_MARKER_COM (1<<6) /* Comment segment */ - -/*** - * Enums - ***/ -/* Sensor types available with cpia2 asics */ -enum sensors { - CPIA2_SENSOR_410, - CPIA2_SENSOR_500 -}; - -/* Asic types available in the CPiA2 architecture */ -#define CPIA2_ASIC_672 0x67 - -/* Device types (stv672, stv676, etc) */ -#define DEVICE_STV_672 0x0001 -#define DEVICE_STV_676 0x0002 - -enum frame_status { - FRAME_EMPTY, - FRAME_READING, /* In the process of being grabbed into */ - FRAME_READY, /* Ready to be read */ - FRAME_ERROR, -}; - -/*** - * Register access (for USB request byte) - ***/ -enum { - CAMERAACCESS_SYSTEM = 0, - CAMERAACCESS_VC, - CAMERAACCESS_VP, - CAMERAACCESS_IDATA -}; - -#define CAMERAACCESS_TYPE_BLOCK 0x00 -#define CAMERAACCESS_TYPE_RANDOM 0x04 -#define CAMERAACCESS_TYPE_MASK 0x08 -#define CAMERAACCESS_TYPE_REPEAT 0x0C - -#define TRANSFER_READ 0 -#define TRANSFER_WRITE 1 - -#define DEFAULT_ALT USBIF_ISO_6 -#define DEFAULT_BRIGHTNESS 0x46 -#define DEFAULT_CONTRAST 0x93 -#define DEFAULT_SATURATION 0x7f - -/* Power state */ -#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER -#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER - - -/******** - * Commands - *******/ -enum { - CPIA2_CMD_NONE = 0, - CPIA2_CMD_GET_VERSION, - CPIA2_CMD_GET_PNP_ID, - CPIA2_CMD_GET_ASIC_TYPE, - CPIA2_CMD_GET_SENSOR, - CPIA2_CMD_GET_VP_DEVICE, - CPIA2_CMD_GET_VP_BRIGHTNESS, - CPIA2_CMD_SET_VP_BRIGHTNESS, - CPIA2_CMD_GET_CONTRAST, - CPIA2_CMD_SET_CONTRAST, - CPIA2_CMD_GET_VP_SATURATION, - CPIA2_CMD_SET_VP_SATURATION, - CPIA2_CMD_GET_VP_GPIO_DIRECTION, - CPIA2_CMD_SET_VP_GPIO_DIRECTION, - CPIA2_CMD_GET_VP_GPIO_DATA, - CPIA2_CMD_SET_VP_GPIO_DATA, - CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - CPIA2_CMD_GET_VC_MP_GPIO_DATA, - CPIA2_CMD_SET_VC_MP_GPIO_DATA, - CPIA2_CMD_ENABLE_PACKET_CTRL, - CPIA2_CMD_GET_FLICKER_MODES, - CPIA2_CMD_SET_FLICKER_MODES, - CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */ - CPIA2_CMD_SET_HI_POWER, - CPIA2_CMD_SET_LOW_POWER, - CPIA2_CMD_CLEAR_V2W_ERR, - CPIA2_CMD_SET_USER_MODE, - CPIA2_CMD_GET_USER_MODE, - CPIA2_CMD_FRAMERATE_REQ, - CPIA2_CMD_SET_COMPRESSION_STATE, - CPIA2_CMD_GET_WAKEUP, - CPIA2_CMD_SET_WAKEUP, - CPIA2_CMD_GET_PW_CONTROL, - CPIA2_CMD_SET_PW_CONTROL, - CPIA2_CMD_GET_SYSTEM_CTRL, - CPIA2_CMD_SET_SYSTEM_CTRL, - CPIA2_CMD_GET_VP_SYSTEM_STATE, - CPIA2_CMD_GET_VP_SYSTEM_CTRL, - CPIA2_CMD_SET_VP_SYSTEM_CTRL, - CPIA2_CMD_GET_VP_EXP_MODES, - CPIA2_CMD_SET_VP_EXP_MODES, - CPIA2_CMD_GET_DEVICE_CONFIG, - CPIA2_CMD_SET_DEVICE_CONFIG, - CPIA2_CMD_SET_SERIAL_ADDR, - CPIA2_CMD_SET_SENSOR_CR1, - CPIA2_CMD_GET_VC_CONTROL, - CPIA2_CMD_SET_VC_CONTROL, - CPIA2_CMD_SET_TARGET_KB, - CPIA2_CMD_SET_DEF_JPEG_OPT, - CPIA2_CMD_REHASH_VP4, - CPIA2_CMD_GET_USER_EFFECTS, - CPIA2_CMD_SET_USER_EFFECTS -}; - -enum user_cmd { - COMMAND_NONE = 0x00000001, - COMMAND_SET_FPS = 0x00000002, - COMMAND_SET_COLOR_PARAMS = 0x00000004, - COMMAND_GET_COLOR_PARAMS = 0x00000008, - COMMAND_SET_FORMAT = 0x00000010, /* size, etc */ - COMMAND_SET_FLICKER = 0x00000020 -}; - -/*** - * Some defines specific to the 676 chip - ***/ -#define CAMACC_CIF 0x01 -#define CAMACC_VGA 0x02 -#define CAMACC_QCIF 0x04 -#define CAMACC_QVGA 0x08 - - -struct cpia2_register { - u8 index; - u8 value; -}; - -struct cpia2_reg_mask { - u8 index; - u8 and_mask; - u8 or_mask; - u8 fill; -}; - -struct cpia2_command { - u32 command; - u8 req_mode; /* (Block or random) | registerBank */ - u8 reg_count; - u8 direction; - u8 start; - union reg_types { - struct cpia2_register registers[32]; - struct cpia2_reg_mask masks[16]; - u8 block_data[64]; - u8 *patch_data; /* points to function defined block */ - } buffer; -}; - -struct camera_params { - struct { - u8 firmware_revision_hi; /* For system register set (bank 0) */ - u8 firmware_revision_lo; - u8 asic_id; /* Video Compressor set (bank 1) */ - u8 asic_rev; - u8 vp_device_hi; /* Video Processor set (bank 2) */ - u8 vp_device_lo; - u8 sensor_flags; - u8 sensor_rev; - } version; - - struct { - u32 device_type; /* enumerated from vendor/product ids. - * Currently, either STV_672 or STV_676 */ - u16 vendor; - u16 product; - u16 device_revision; - } pnp_id; - - struct { - u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */ - u8 contrast; /* Note: this is CPIA2_VP_YRANGE */ - u8 saturation; /* CPIA2_VP_SATURATION */ - } color_params; - - struct { - u8 cam_register; - u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ - } flicker_control; - - struct { - u8 jpeg_options; - u8 creep_period; - u8 user_squeeze; - u8 inhibit_htables; - } compression; - - struct { - u8 ohsize; /* output image size */ - u8 ovsize; - u8 hcrop; /* cropping start_pos/4 */ - u8 vcrop; - u8 hphase; /* scaling registers */ - u8 vphase; - u8 hispan; - u8 vispan; - u8 hicrop; - u8 vicrop; - u8 hifraction; - u8 vifraction; - } image_size; - - struct { - int width; /* actual window width */ - int height; /* actual window height */ - } roi; - - struct { - u8 video_mode; - u8 frame_rate; - u8 video_size; /* Not a register, just a convenience for cropped sizes */ - u8 gpio_direction; - u8 gpio_data; - u8 system_ctrl; - u8 system_state; - u8 lowlight_boost; /* Bool: 0 = off, 1 = on */ - u8 device_config; - u8 exposure_modes; - u8 user_effects; - } vp_params; - - struct { - u8 pw_control; - u8 wakeup; - u8 vc_control; - u8 vc_mp_direction; - u8 vc_mp_data; - u8 quality; - } vc_params; - - struct { - u8 power_mode; - u8 system_ctrl; - u8 stream_mode; /* This is the current alternate for usb drivers */ - u8 allow_corrupt; - } camera_state; -}; - -#define NUM_SBUF 2 - -struct cpia2_sbuf { - char *data; - struct urb *urb; -}; - -struct framebuf { - u64 ts; - unsigned long seq; - int num; - int length; - int max_length; - volatile enum frame_status status; - u8 *data; - struct framebuf *next; -}; - -struct camera_data { - /* locks */ - struct v4l2_device v4l2_dev; - struct mutex v4l2_lock; /* serialize file operations */ - struct v4l2_ctrl_handler hdl; - struct { - /* Lights control cluster */ - struct v4l2_ctrl *top_light; - struct v4l2_ctrl *bottom_light; - }; - struct v4l2_ctrl *usb_alt; - - /* camera status */ - int first_image_seen; - enum sensors sensor_type; - u8 flush; - struct v4l2_fh *stream_fh; - u8 mmapped; - int streaming; /* 0 = no, 1 = yes */ - int xfer_mode; /* XFER_BULK or XFER_ISOC */ - struct camera_params params; /* camera settings */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - struct video_device vdev; /* v4l videodev */ - u32 width; - u32 height; /* Its size */ - __u32 pixelformat; /* Format fourcc */ - - /* USB */ - struct usb_device *dev; - unsigned char iface; - unsigned int cur_alt; - unsigned int old_alt; - struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */ - - wait_queue_head_t wq_stream; - - /* Buffering */ - u32 frame_size; - int num_frames; - unsigned long frame_count; - u8 *frame_buffer; /* frame buffer data */ - struct framebuf *buffers; - struct framebuf * volatile curbuff; - struct framebuf *workbuff; - - /* MJPEG Extension */ - int APPn; /* Number of APP segment to be written, must be 0..15 */ - int APP_len; /* Length of data in JPEG APPn segment */ - char APP_data[60]; /* Data in the JPEG APPn segment. */ - - int COM_len; /* Length of data in JPEG COM segment */ - char COM_data[60]; /* Data in JPEG COM segment */ -}; - -/* v4l */ -int cpia2_register_camera(struct camera_data *cam); -void cpia2_unregister_camera(struct camera_data *cam); -void cpia2_camera_release(struct v4l2_device *v4l2_dev); - -/* core */ -int cpia2_reset_camera(struct camera_data *cam); -int cpia2_set_low_power(struct camera_data *cam); -void cpia2_dbg_dump_registers(struct camera_data *cam); -int cpia2_match_video_size(int width, int height); -void cpia2_set_camera_state(struct camera_data *cam); -void cpia2_save_camera_state(struct camera_data *cam); -void cpia2_set_color_params(struct camera_data *cam); -void cpia2_set_brightness(struct camera_data *cam, unsigned char value); -void cpia2_set_contrast(struct camera_data *cam, unsigned char value); -void cpia2_set_saturation(struct camera_data *cam, unsigned char value); -int cpia2_set_flicker_mode(struct camera_data *cam, int mode); -void cpia2_set_format(struct camera_data *cam); -int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); -int cpia2_do_command(struct camera_data *cam, - unsigned int command, - unsigned char direction, unsigned char param); -void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf); -struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); -int cpia2_init_camera(struct camera_data *cam); -int cpia2_allocate_buffers(struct camera_data *cam); -void cpia2_free_buffers(struct camera_data *cam); -long cpia2_read(struct camera_data *cam, - char __user *buf, unsigned long count, int noblock); -__poll_t cpia2_poll(struct camera_data *cam, - struct file *filp, poll_table *wait); -int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); -void cpia2_set_property_flip(struct camera_data *cam, int prop_val); -void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); -int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); -int cpia2_set_fps(struct camera_data *cam, int framerate); - -/* usb */ -int cpia2_usb_init(void); -void cpia2_usb_cleanup(void); -int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers, - u8 request, u8 start, u8 count, u8 direction); -int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate); -int cpia2_usb_stream_stop(struct camera_data *cam); -int cpia2_usb_stream_pause(struct camera_data *cam); -int cpia2_usb_stream_resume(struct camera_data *cam); -int cpia2_usb_change_streaming_alternate(struct camera_data *cam, - unsigned int alt); - - -/* ----------------------- debug functions ---------------------- */ -#ifdef _CPIA2_DEBUG_ -#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args) -#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args) -#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args) -#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args) -#else -#define ALOG(fmt,args...) printk(fmt,##args) -#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args) -#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args) -#define DBG(fmn,args...) do {} while(0) -#endif -/* No function or lineno, for shorter lines */ -#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args) - -#endif diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c deleted file mode 100644 index b5a2d06fb356..000000000000 --- a/drivers/media/usb/cpia2/cpia2_core.c +++ /dev/null @@ -1,2434 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/**************************************************************************** - * - * Filename: cpia2_core.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - * - ****************************************************************************/ - -#include "cpia2.h" - -#include -#include -#include -#include -#include -#include - -#define FIRMWARE "cpia2/stv0672_vp4.bin" -MODULE_FIRMWARE(FIRMWARE); - -/* #define _CPIA2_DEBUG_ */ - -#ifdef _CPIA2_DEBUG_ - -static const char *block_name[] = { - "System", - "VC", - "VP", - "IDATA" -}; -#endif - -static unsigned int debugs_on; /* default 0 - DEBUG_REG */ - - -/****************************************************************************** - * - * Forward Declarations - * - *****************************************************************************/ -static int apply_vp_patch(struct camera_data *cam); -static int set_default_user_mode(struct camera_data *cam); -static int set_vw_size(struct camera_data *cam, int size); -static int configure_sensor(struct camera_data *cam, - int reqwidth, int reqheight); -static int config_sensor_410(struct camera_data *cam, - int reqwidth, int reqheight); -static int config_sensor_500(struct camera_data *cam, - int reqwidth, int reqheight); -static int set_all_properties(struct camera_data *cam); -static void wake_system(struct camera_data *cam); -static void set_lowlight_boost(struct camera_data *cam); -static void reset_camera_struct(struct camera_data *cam); -static int cpia2_set_high_power(struct camera_data *cam); - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long kva, ret; - - kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - ret = __pa(kva); - return ret; -} - -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - /* Round it off to PAGE_SIZE */ - size = PAGE_ALIGN(size); - - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - - while ((long)size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - size = PAGE_ALIGN(size); - - adr = (unsigned long) mem; - while ((long)size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/****************************************************************************** - * - * cpia2_do_command - * - * Send an arbitrary command to the camera. For commands that read from - * the camera, copy the buffers into the proper param structures. - *****************************************************************************/ -int cpia2_do_command(struct camera_data *cam, - u32 command, u8 direction, u8 param) -{ - int retval = 0; - struct cpia2_command cmd; - unsigned int device = cam->params.pnp_id.device_type; - - cmd.command = command; - cmd.reg_count = 2; /* default */ - cmd.direction = direction; - - /*** - * Set up the command. - ***/ - switch (command) { - case CPIA2_CMD_GET_VERSION: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.start = CPIA2_SYSTEM_DEVICE_HI; - break; - case CPIA2_CMD_GET_PNP_ID: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 8; - cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI; - break; - case CPIA2_CMD_GET_ASIC_TYPE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_ASIC_ID; - break; - case CPIA2_CMD_GET_SENSOR: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.start = CPIA2_VP_SENSOR_FLAGS; - break; - case CPIA2_CMD_GET_VP_DEVICE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.start = CPIA2_VP_DEVICEH; - break; - case CPIA2_CMD_SET_VP_BRIGHTNESS: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_BRIGHTNESS: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_EXPOSURE_TARGET; - else - cmd.start = CPIA2_VP5_EXPOSURE_TARGET; - break; - case CPIA2_CMD_SET_CONTRAST: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_CONTRAST: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_YRANGE; - break; - case CPIA2_CMD_SET_VP_SATURATION: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_SATURATION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP_SATURATION; - else - cmd.start = CPIA2_VP5_MCUVSATURATION; - break; - case CPIA2_CMD_SET_VP_GPIO_DATA: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_GPIO_DATA: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_GPIO_DATA; - break; - case CPIA2_CMD_SET_VP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_GPIO_DIRECTION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_GPIO_DIRECTION; - break; - case CPIA2_CMD_SET_VC_MP_GPIO_DATA: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VC_MP_GPIO_DATA: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_MP_DATA; - break; - case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_MP_DIR; - break; - case CPIA2_CMD_ENABLE_PACKET_CTRL: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL; - cmd.reg_count = 1; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_FLICKER_MODES: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_FLICKER_MODES: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_FLICKER_MODES; - break; - case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 2; - cmd.start = 0; - cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; - cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | - CPIA2_VC_ST_CTRL_EOF_DETECT | - CPIA2_VC_ST_CTRL_FIFO_ENABLE; - break; - case CPIA2_CMD_SET_HI_POWER: - cmd.req_mode = - CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; - cmd.reg_count = 2; - cmd.buffer.registers[0].index = - CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.registers[1].index = - CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; - cmd.buffer.registers[1].value = - CPIA2_SYSTEM_CONTROL_HIGH_POWER; - break; - case CPIA2_CMD_SET_LOW_POWER: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.block_data[0] = 0; - break; - case CPIA2_CMD_CLEAR_V2W_ERR: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; - break; - case CPIA2_CMD_SET_USER_MODE: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_USER_MODE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_USER_MODE; - else - cmd.start = CPIA2_VP5_USER_MODE; - break; - case CPIA2_CMD_FRAMERATE_REQ: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_FRAMERATE_REQUEST; - else - cmd.start = CPIA2_VP5_FRAMERATE_REQUEST; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_WAKEUP: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_WAKEUP: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_WAKEUP; - break; - case CPIA2_CMD_SET_PW_CONTROL: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_PW_CONTROL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_PW_CTRL; - break; - case CPIA2_CMD_GET_VP_SYSTEM_STATE: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_SYSTEMSTATE; - break; - case CPIA2_CMD_SET_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_SYSTEM_CTRL: - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; - break; - case CPIA2_CMD_SET_VP_SYSTEM_CTRL: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_SYSTEM_CTRL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_SYSTEMCTRL; - break; - case CPIA2_CMD_SET_VP_EXP_MODES: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VP_EXP_MODES: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_EXPOSURE_MODES; - break; - case CPIA2_CMD_SET_DEVICE_CONFIG: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_DEVICE_CONFIG: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_DEVICE_CONFIG; - break; - case CPIA2_CMD_SET_SERIAL_ADDR: - cmd.buffer.block_data[0] = param; - cmd.req_mode = - CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 1; - cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR; - break; - case CPIA2_CMD_SET_SENSOR_CR1: - cmd.buffer.block_data[0] = param; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_SENSOR_CR1; - break; - case CPIA2_CMD_SET_VC_CONTROL: - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_VC_CONTROL: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.start = CPIA2_VC_VC_CTRL; - break; - case CPIA2_CMD_SET_TARGET_KB: - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 1; - cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB; - cmd.buffer.registers[0].value = param; - break; - case CPIA2_CMD_SET_DEF_JPEG_OPT: - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.reg_count = 4; - cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT; - cmd.buffer.registers[0].value = - CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE; - cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE; - cmd.buffer.registers[1].value = 20; - cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD; - cmd.buffer.registers[2].value = 2; - cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT; - cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT; - break; - case CPIA2_CMD_REHASH_VP4: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - cmd.start = CPIA2_VP_REHASH_VALUES; - cmd.buffer.block_data[0] = param; - break; - case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as - this register can also affect - flicker modes */ - cmd.buffer.block_data[0] = param; - fallthrough; - case CPIA2_CMD_GET_USER_EFFECTS: - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 1; - if (device == DEVICE_STV_672) - cmd.start = CPIA2_VP4_USER_EFFECTS; - else - cmd.start = CPIA2_VP5_USER_EFFECTS; - break; - default: - LOG("DoCommand received invalid command\n"); - return -EINVAL; - } - - retval = cpia2_send_command(cam, &cmd); - if (retval) { - return retval; - } - - /*** - * Now copy any results from a read into the appropriate param struct. - ***/ - switch (command) { - case CPIA2_CMD_GET_VERSION: - cam->params.version.firmware_revision_hi = - cmd.buffer.block_data[0]; - cam->params.version.firmware_revision_lo = - cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_PNP_ID: - cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) | - cmd.buffer.block_data[1]; - cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) | - cmd.buffer.block_data[3]; - cam->params.pnp_id.device_revision = - (cmd.buffer.block_data[4] << 8) | - cmd.buffer.block_data[5]; - if (cam->params.pnp_id.vendor == 0x553) { - if (cam->params.pnp_id.product == 0x100) { - cam->params.pnp_id.device_type = DEVICE_STV_672; - } else if (cam->params.pnp_id.product == 0x140 || - cam->params.pnp_id.product == 0x151) { - cam->params.pnp_id.device_type = DEVICE_STV_676; - } - } - break; - case CPIA2_CMD_GET_ASIC_TYPE: - cam->params.version.asic_id = cmd.buffer.block_data[0]; - cam->params.version.asic_rev = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_SENSOR: - cam->params.version.sensor_flags = cmd.buffer.block_data[0]; - cam->params.version.sensor_rev = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_VP_DEVICE: - cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; - cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; - break; - case CPIA2_CMD_GET_VP_GPIO_DATA: - cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_GPIO_DIRECTION: - cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: - cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_MP_GPIO_DATA: - cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_FLICKER_MODES: - cam->params.flicker_control.cam_register = - cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_WAKEUP: - cam->params.vc_params.wakeup = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_PW_CONTROL: - cam->params.vc_params.pw_control = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_SYSTEM_CTRL: - cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SYSTEM_STATE: - cam->params.vp_params.system_state = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SYSTEM_CTRL: - cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_EXP_MODES: - cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_DEVICE_CONFIG: - cam->params.vp_params.device_config = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VC_CONTROL: - cam->params.vc_params.vc_control = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_USER_MODE: - cam->params.vp_params.video_mode = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_USER_EFFECTS: - cam->params.vp_params.user_effects = cmd.buffer.block_data[0]; - break; - default: - break; - } - return retval; -} - -/****************************************************************************** - * - * cpia2_send_command - * - *****************************************************************************/ - -#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") -#define BINDEX(cmd) (cmd->req_mode & 0x03) - -int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) -{ - u8 count; - u8 start; - u8 *buffer; - int retval; - - switch (cmd->req_mode & 0x0c) { - case CAMERAACCESS_TYPE_RANDOM: - count = cmd->reg_count * sizeof(struct cpia2_register); - start = 0; - buffer = (u8 *) & cmd->buffer; - if (debugs_on & DEBUG_REG) - DBG("%s Random: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_BLOCK: - count = cmd->reg_count; - start = cmd->start; - buffer = cmd->buffer.block_data; - if (debugs_on & DEBUG_REG) - DBG("%s Block: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_MASK: - count = cmd->reg_count * sizeof(struct cpia2_reg_mask); - start = 0; - buffer = (u8 *) & cmd->buffer; - if (debugs_on & DEBUG_REG) - DBG("%s Mask: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ - count = cmd->reg_count; - start = cmd->start; - buffer = cmd->buffer.block_data; - if (debugs_on & DEBUG_REG) - DBG("%s Repeat: Register block %s\n", DIR(cmd), - block_name[BINDEX(cmd)]); - break; - default: - LOG("%s: invalid request mode\n",__func__); - return -EINVAL; - } - - retval = cpia2_usb_transfer_cmd(cam, - buffer, - cmd->req_mode, - start, count, cmd->direction); -#ifdef _CPIA2_DEBUG_ - if (debugs_on & DEBUG_REG) { - int i; - for (i = 0; i < cmd->reg_count; i++) { - if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) - KINFO("%s Block: [0x%02X] = 0x%02X\n", - DIR(cmd), start + i, buffer[i]); - if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) - KINFO("%s Random: [0x%02X] = 0x%02X\n", - DIR(cmd), cmd->buffer.registers[i].index, - cmd->buffer.registers[i].value); - } - } -#endif - - return retval; -}; - -/************* - * Functions to implement camera functionality - *************/ -/****************************************************************************** - * - * cpia2_get_version_info - * - *****************************************************************************/ -static void cpia2_get_version_info(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0); -} - -/****************************************************************************** - * - * cpia2_reset_camera - * - * Called at least during the open process, sets up initial params. - *****************************************************************************/ -int cpia2_reset_camera(struct camera_data *cam) -{ - u8 tmp_reg; - int retval = 0; - int target_kb; - int i; - struct cpia2_command cmd; - - /*** - * VC setup - ***/ - retval = configure_sensor(cam, - cam->params.roi.width, - cam->params.roi.height); - if (retval < 0) { - ERR("Couldn't configure sensor, error=%d\n", retval); - return retval; - } - - /* Clear FIFO and route/enable stream block */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - cmd.reg_count = 2; - cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; - cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; - cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | - CPIA2_VC_ST_CTRL_DST_USB | - CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE; - - cpia2_send_command(cam, &cmd); - - cpia2_set_high_power(cam); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - /* Enable button notification */ - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; - cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL; - cmd.buffer.registers[0].value = - CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - } - - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - retval = apply_vp_patch(cam); - - /* wait for vp to go to sleep */ - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - /*** - * If this is a 676, apply VP5 fixes before we start streaming - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_676) { - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - - /* The following writes improve the picture */ - cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL; - cmd.buffer.registers[0].value = 0; /* reduce from the default - * rec 601 pedestal of 16 */ - cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE; - cmd.buffer.registers[1].value = 0x92; /* increase from 100% to - * (256/256 - 31) to fill - * available range */ - cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING; - cmd.buffer.registers[2].value = 0xFF; /* Increase from the - * default rec 601 ceiling - * of 240 */ - cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION; - cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec - * 601 100% level (128) - * to 145-192 */ - cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP; - cmd.buffer.registers[4].value = 0x80; /* Inhibit the - * anti-flicker */ - - /* The following 4 writes are a fix to allow QVGA to work at 30 fps */ - cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H; - cmd.buffer.registers[5].value = 0x01; - cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L; - cmd.buffer.registers[6].value = 0xE3; - cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA; - cmd.buffer.registers[7].value = 0x02; - cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA; - cmd.buffer.registers[8].value = 0xFC; - - cmd.direction = TRANSFER_WRITE; - cmd.reg_count = 9; - - cpia2_send_command(cam, &cmd); - } - - /* Activate all settings and start the data stream */ - /* Set user mode */ - set_default_user_mode(cam); - - /* Give VP time to wake up */ - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - set_all_properties(cam); - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); - DBG("After SetAllProperties(cam), user mode is 0x%0X\n", - cam->params.vp_params.video_mode); - - /*** - * Set audio regulator off. This and the code to set the compresison - * state are too complex to form a CPIA2_CMD_, and seem to be somewhat - * intertwined. This stuff came straight from the windows driver. - ***/ - /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); - tmp_reg = cam->params.vp_params.system_ctrl; - cmd.buffer.registers[0].value = tmp_reg & - (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF)); - - cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); - cmd.buffer.registers[1].value = cam->params.vp_params.device_config | - CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE; - cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL; - cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG; - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - cmd.reg_count = 2; - cmd.direction = TRANSFER_WRITE; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - - /* Set the correct I2C address in the CPiA-2 system register */ - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR); - - /* Now have sensor access - set bit to turn the audio regulator off */ - cpia2_do_command(cam, - CPIA2_CMD_SET_SENSOR_CR1, - TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR); - - /* Set the correct I2C address in the CPiA-2 system register */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88 - else - cpia2_do_command(cam, - CPIA2_CMD_SET_SERIAL_ADDR, - TRANSFER_WRITE, - CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a - - /* increase signal drive strength */ - if (cam->params.pnp_id.device_type == DEVICE_STV_676) - cpia2_do_command(cam, - CPIA2_CMD_SET_VP_EXP_MODES, - TRANSFER_WRITE, - CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP); - - /* Start autoexposure */ - cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); - cmd.buffer.registers[0].value = cam->params.vp_params.device_config & - (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF); - - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); - cmd.buffer.registers[1].value = - cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL; - - cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG; - cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL; - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; - cmd.reg_count = 2; - cmd.direction = TRANSFER_WRITE; - - cpia2_send_command(cam, &cmd); - - /* Set compression state */ - cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0); - if (cam->params.compression.inhibit_htables) { - tmp_reg = cam->params.vc_params.vc_control | - CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; - } else { - tmp_reg = cam->params.vc_params.vc_control & - ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; - } - cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); - - /* Set target size (kb) on vc - This is a heuristic based on the quality parameter and the raw - framesize in kB divided by 16 (the compression factor when the - quality is 100%) */ - target_kb = (cam->width * cam->height * 2 / 16384) * - cam->params.vc_params.quality / 100; - if (target_kb < 1) - target_kb = 1; - cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, - TRANSFER_WRITE, target_kb); - - /* Wiggle VC Reset */ - /*** - * First read and wait a bit. - ***/ - for (i = 0; i < 50; i++) { - cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL, - TRANSFER_READ, 0); - } - - tmp_reg = cam->params.vc_params.pw_control; - tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N; - - cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); - - tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N; - cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); - - cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0); - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); - DBG("After VC RESET, user mode is 0x%0X\n", - cam->params.vp_params.video_mode); - - return retval; -} - -/****************************************************************************** - * - * cpia2_set_high_power - * - *****************************************************************************/ -static int cpia2_set_high_power(struct camera_data *cam) -{ - int i; - for (i = 0; i <= 50; i++) { - /* Read system status */ - cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0); - - /* If there is an error, clear it */ - if(cam->params.camera_state.system_ctrl & - CPIA2_SYSTEM_CONTROL_V2W_ERR) - cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR, - TRANSFER_WRITE, 0); - - /* Try to set high power mode */ - cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, - TRANSFER_WRITE, 1); - - /* Try to read something in VP to check if everything is awake */ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE, - TRANSFER_READ, 0); - if (cam->params.vp_params.system_state & - CPIA2_VP_SYSTEMSTATE_HK_ALIVE) { - break; - } else if (i == 50) { - cam->params.camera_state.power_mode = LO_POWER_MODE; - ERR("Camera did not wake up\n"); - return -EIO; - } - } - - DBG("System now in high power state\n"); - cam->params.camera_state.power_mode = HI_POWER_MODE; - return 0; -} - -/****************************************************************************** - * - * cpia2_set_low_power - * - *****************************************************************************/ -int cpia2_set_low_power(struct camera_data *cam) -{ - cam->params.camera_state.power_mode = LO_POWER_MODE; - cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0); - return 0; -} - -/****************************************************************************** - * - * apply_vp_patch - * - *****************************************************************************/ -static int cpia2_send_onebyte_command(struct camera_data *cam, - struct cpia2_command *cmd, - u8 start, u8 datum) -{ - cmd->buffer.block_data[0] = datum; - cmd->start = start; - cmd->reg_count = 1; - return cpia2_send_command(cam, cmd); -} - -static int apply_vp_patch(struct camera_data *cam) -{ - const struct firmware *fw; - const char fw_name[] = FIRMWARE; - int i, ret; - struct cpia2_command cmd; - - ret = request_firmware(&fw, fw_name, &cam->dev->dev); - if (ret) { - printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n", - fw_name); - return ret; - } - - cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP; - cmd.direction = TRANSFER_WRITE; - - /* First send the start address... */ - cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ - cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ - - /* ... followed by the data payload */ - for (i = 2; i < fw->size; i += 64) { - cmd.start = 0x0C; /* Data */ - cmd.reg_count = min_t(uint, 64, fw->size - i); - memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count); - cpia2_send_command(cam, &cmd); - } - - /* Next send the start address... */ - cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ - cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ - - /* ... followed by the 'goto' command */ - cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1); - - release_firmware(fw); - return 0; -} - -/****************************************************************************** - * - * set_default_user_mode - * - *****************************************************************************/ -static int set_default_user_mode(struct camera_data *cam) -{ - unsigned char user_mode; - unsigned char frame_rate; - int width = cam->params.roi.width; - int height = cam->params.roi.height; - - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - case CPIA2_VP_SENSOR_FLAGS_407: - case CPIA2_VP_SENSOR_FLAGS_409: - case CPIA2_VP_SENSOR_FLAGS_410: - if ((width > STV_IMAGE_QCIF_COLS) - || (height > STV_IMAGE_QCIF_ROWS)) { - user_mode = CPIA2_VP_USER_MODE_CIF; - } else { - user_mode = CPIA2_VP_USER_MODE_QCIFDS; - } - frame_rate = CPIA2_VP_FRAMERATE_30; - break; - case CPIA2_VP_SENSOR_FLAGS_500: - if ((width > STV_IMAGE_CIF_COLS) - || (height > STV_IMAGE_CIF_ROWS)) { - user_mode = CPIA2_VP_USER_MODE_VGA; - } else { - user_mode = CPIA2_VP_USER_MODE_QVGADS; - } - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - frame_rate = CPIA2_VP_FRAMERATE_15; - else - frame_rate = CPIA2_VP_FRAMERATE_30; - break; - default: - LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, - cam->params.version.sensor_flags); - return -EINVAL; - } - - DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n", - cam->params.version.sensor_flags, user_mode, frame_rate); - cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE, - user_mode); - if(cam->params.vp_params.frame_rate > 0 && - frame_rate > cam->params.vp_params.frame_rate) - frame_rate = cam->params.vp_params.frame_rate; - - cpia2_set_fps(cam, frame_rate); - -// if (cam->params.pnp_id.device_type == DEVICE_STV_676) -// cpia2_do_command(cam, -// CPIA2_CMD_SET_VP_SYSTEM_CTRL, -// TRANSFER_WRITE, -// CPIA2_VP_SYSTEMCTRL_HK_CONTROL | -// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL); - - return 0; -} - -/****************************************************************************** - * - * cpia2_match_video_size - * - * return the best match, where 'best' is as always - * the largest that is not bigger than what is requested. - *****************************************************************************/ -int cpia2_match_video_size(int width, int height) -{ - if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS) - return VIDEOSIZE_VGA; - - if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS) - return VIDEOSIZE_CIF; - - if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS) - return VIDEOSIZE_QVGA; - - if (width >= 288 && height >= 216) - return VIDEOSIZE_288_216; - - if (width >= 256 && height >= 192) - return VIDEOSIZE_256_192; - - if (width >= 224 && height >= 168) - return VIDEOSIZE_224_168; - - if (width >= 192 && height >= 144) - return VIDEOSIZE_192_144; - - if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS) - return VIDEOSIZE_QCIF; - - return -1; -} - -/****************************************************************************** - * - * SetVideoSize - * - *****************************************************************************/ -static int set_vw_size(struct camera_data *cam, int size) -{ - int retval = 0; - - cam->params.vp_params.video_size = size; - - switch (size) { - case VIDEOSIZE_VGA: - DBG("Setting size to VGA\n"); - cam->params.roi.width = STV_IMAGE_VGA_COLS; - cam->params.roi.height = STV_IMAGE_VGA_ROWS; - cam->width = STV_IMAGE_VGA_COLS; - cam->height = STV_IMAGE_VGA_ROWS; - break; - case VIDEOSIZE_CIF: - DBG("Setting size to CIF\n"); - cam->params.roi.width = STV_IMAGE_CIF_COLS; - cam->params.roi.height = STV_IMAGE_CIF_ROWS; - cam->width = STV_IMAGE_CIF_COLS; - cam->height = STV_IMAGE_CIF_ROWS; - break; - case VIDEOSIZE_QVGA: - DBG("Setting size to QVGA\n"); - cam->params.roi.width = STV_IMAGE_QVGA_COLS; - cam->params.roi.height = STV_IMAGE_QVGA_ROWS; - cam->width = STV_IMAGE_QVGA_COLS; - cam->height = STV_IMAGE_QVGA_ROWS; - break; - case VIDEOSIZE_288_216: - cam->params.roi.width = 288; - cam->params.roi.height = 216; - cam->width = 288; - cam->height = 216; - break; - case VIDEOSIZE_256_192: - cam->width = 256; - cam->height = 192; - cam->params.roi.width = 256; - cam->params.roi.height = 192; - break; - case VIDEOSIZE_224_168: - cam->width = 224; - cam->height = 168; - cam->params.roi.width = 224; - cam->params.roi.height = 168; - break; - case VIDEOSIZE_192_144: - cam->width = 192; - cam->height = 144; - cam->params.roi.width = 192; - cam->params.roi.height = 144; - break; - case VIDEOSIZE_QCIF: - DBG("Setting size to QCIF\n"); - cam->params.roi.width = STV_IMAGE_QCIF_COLS; - cam->params.roi.height = STV_IMAGE_QCIF_ROWS; - cam->width = STV_IMAGE_QCIF_COLS; - cam->height = STV_IMAGE_QCIF_ROWS; - break; - default: - retval = -EINVAL; - } - return retval; -} - -/****************************************************************************** - * - * configure_sensor - * - *****************************************************************************/ -static int configure_sensor(struct camera_data *cam, - int req_width, int req_height) -{ - int retval; - - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - case CPIA2_VP_SENSOR_FLAGS_407: - case CPIA2_VP_SENSOR_FLAGS_409: - case CPIA2_VP_SENSOR_FLAGS_410: - retval = config_sensor_410(cam, req_width, req_height); - break; - case CPIA2_VP_SENSOR_FLAGS_500: - retval = config_sensor_500(cam, req_width, req_height); - break; - default: - return -EINVAL; - } - - return retval; -} - -/****************************************************************************** - * - * config_sensor_410 - * - *****************************************************************************/ -static int config_sensor_410(struct camera_data *cam, - int req_width, int req_height) -{ - struct cpia2_command cmd; - int i = 0; - int image_size; - int image_type; - int width = req_width; - int height = req_height; - - /*** - * Make sure size doesn't exceed CIF. - ***/ - if (width > STV_IMAGE_CIF_COLS) - width = STV_IMAGE_CIF_COLS; - if (height > STV_IMAGE_CIF_ROWS) - height = STV_IMAGE_CIF_ROWS; - - image_size = cpia2_match_video_size(width, height); - - DBG("Config 410: width = %d, height = %d\n", width, height); - DBG("Image size returned is %d\n", image_size); - if (image_size >= 0) { - set_vw_size(cam, image_size); - width = cam->params.roi.width; - height = cam->params.roi.height; - - DBG("After set_vw_size(), width = %d, height = %d\n", - width, height); - if (width <= 176 && height <= 144) { - DBG("image type = VIDEOSIZE_QCIF\n"); - image_type = VIDEOSIZE_QCIF; - } - else if (width <= 320 && height <= 240) { - DBG("image type = VIDEOSIZE_QVGA\n"); - image_type = VIDEOSIZE_QVGA; - } - else { - DBG("image type = VIDEOSIZE_CIF\n"); - image_type = VIDEOSIZE_CIF; - } - } else { - ERR("ConfigSensor410 failed\n"); - return -EINVAL; - } - - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - - /* VC Format */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; - if (image_type == VIDEOSIZE_CIF) { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_FORMAT_UFIRST | - CPIA2_VC_VC_FORMAT_SHORTLINE); - } else { - cmd.buffer.registers[i++].value = - (u8) CPIA2_VC_VC_FORMAT_UFIRST; - } - - /* VC Clocks */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; - if (image_type == VIDEOSIZE_QCIF) { - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.buffer.registers[i++].value= - (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_672_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - DBG("VC_Clocks (0xc4) should be B\n"); - } - else { - cmd.buffer.registers[i++].value= - (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - } - } else { - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_CLOCKS_LOGDIV0); - } - else { - cmd.buffer.registers[i++].value = - (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | - CPIA2_VC_VC_676_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV0); - } - } - DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value); - - /* Input reqWidth from VC */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (STV_IMAGE_QCIF_COLS / 4); - else - cmd.buffer.registers[i++].value = - (u8) (STV_IMAGE_CIF_COLS / 4); - - /* Timings */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 208; - else - cmd.buffer.registers[i++].value = (u8) 160; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 160; - else - cmd.buffer.registers[i++].value = (u8) 64; - - /* Output Image Size */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; - cmd.buffer.registers[i++].value = cam->params.roi.width / 4; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; - cmd.buffer.registers[i++].value = cam->params.roi.height / 4; - - /* Cropping */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); - else - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); - else - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); - - /* Scaling registers (defaults) */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; - cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; - cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ - - cmd.reg_count = i; - - cpia2_send_command(cam, &cmd); - - return i; -} - - -/****************************************************************************** - * - * config_sensor_500(cam) - * - *****************************************************************************/ -static int config_sensor_500(struct camera_data *cam, - int req_width, int req_height) -{ - struct cpia2_command cmd; - int i = 0; - int image_size = VIDEOSIZE_CIF; - int image_type = VIDEOSIZE_VGA; - int width = req_width; - int height = req_height; - unsigned int device = cam->params.pnp_id.device_type; - - image_size = cpia2_match_video_size(width, height); - - if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS) - image_type = VIDEOSIZE_VGA; - else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS) - image_type = VIDEOSIZE_CIF; - else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS) - image_type = VIDEOSIZE_QVGA; - else - image_type = VIDEOSIZE_QCIF; - - if (image_size >= 0) { - set_vw_size(cam, image_size); - width = cam->params.roi.width; - height = cam->params.roi.height; - } else { - ERR("ConfigSensor500 failed\n"); - return -EINVAL; - } - - DBG("image_size = %d, width = %d, height = %d, type = %d\n", - image_size, width, height, image_type); - - cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; - cmd.direction = TRANSFER_WRITE; - i = 0; - - /* VC Format */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; - cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING; - i++; - - /* VC Clocks */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; - if (device == DEVICE_STV_672) { - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1; - else - cmd.buffer.registers[i].value = - (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV3); - } else { - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0; - else - cmd.buffer.registers[i].value = - (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING | - CPIA2_VC_VC_CLOCKS_LOGDIV2); - } - i++; - - DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value); - - /* Input width from VP */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i].value = - (u8) (STV_IMAGE_VGA_COLS / 4); - else - cmd.buffer.registers[i].value = - (u8) (STV_IMAGE_QVGA_COLS / 4); - i++; - DBG("Input width = %d\n", cmd.buffer.registers[i-1].value); - - /* Timings */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 2; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 250; - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = (u8) 125; - else - cmd.buffer.registers[i++].value = (u8) 160; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 2; - else - cmd.buffer.registers[i++].value = (u8) 1; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = (u8) 12; - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = (u8) 64; - else - cmd.buffer.registers[i++].value = (u8) 6; - - /* Output Image Size */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4; - else - cmd.buffer.registers[i++].value = width / 4; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; - if (image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4; - else - cmd.buffer.registers[i++].value = height / 4; - - /* Cropping */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2); - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2); - else if (image_type == VIDEOSIZE_CIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); - else /*if (image_type == VIDEOSIZE_QCIF)*/ - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; - if (image_type == VIDEOSIZE_VGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2); - else if (image_type == VIDEOSIZE_QVGA) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2); - else if (image_type == VIDEOSIZE_CIF) - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); - else /*if (image_type == VIDEOSIZE_QCIF)*/ - cmd.buffer.registers[i++].value = - (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); - - /* Scaling registers (defaults) */ - cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 36; - else - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 32; - else - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 26; - else - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 21; - else - cmd.buffer.registers[i++].value = (u8) 31; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; - cmd.buffer.registers[i++].value = (u8) 0; - - cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */ - else - cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ - - cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; - if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) - cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */ - else - cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ - - cmd.reg_count = i; - - cpia2_send_command(cam, &cmd); - - return i; -} - - -/****************************************************************************** - * - * setallproperties - * - * This sets all user changeable properties to the values in cam->params. - *****************************************************************************/ -static int set_all_properties(struct camera_data *cam) -{ - /** - * Don't set target_kb here, it will be set later. - * framerate and user_mode were already set (set_default_user_mode). - **/ - - cpia2_usb_change_streaming_alternate(cam, - cam->params.camera_state.stream_mode); - - cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - TRANSFER_WRITE, cam->params.vp_params.gpio_direction); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, - cam->params.vp_params.gpio_data); - - v4l2_ctrl_handler_setup(&cam->hdl); - - wake_system(cam); - - set_lowlight_boost(cam); - - return 0; -} - -/****************************************************************************** - * - * cpia2_save_camera_state - * - *****************************************************************************/ -void cpia2_save_camera_state(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, - 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0); - /* Don't get framerate or target_kb. Trust the values we already have */ -} - - -/****************************************************************************** - * - * cpia2_set_flicker_mode - * - *****************************************************************************/ -int cpia2_set_flicker_mode(struct camera_data *cam, int mode) -{ - unsigned char cam_reg; - int err = 0; - - if(cam->params.pnp_id.device_type != DEVICE_STV_672) - return -EINVAL; - - /* Set the appropriate bits in FLICKER_MODES, preserving the rest */ - if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, - TRANSFER_READ, 0))) - return err; - cam_reg = cam->params.flicker_control.cam_register; - - switch(mode) { - case NEVER_FLICKER: - cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; - break; - case FLICKER_60: - cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; - break; - case FLICKER_50: - cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; - cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ; - break; - default: - return -EINVAL; - } - - if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES, - TRANSFER_WRITE, cam_reg))) - return err; - - /* Set the appropriate bits in EXP_MODES, preserving the rest */ - if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES, - TRANSFER_READ, 0))) - return err; - cam_reg = cam->params.vp_params.exposure_modes; - - if (mode == NEVER_FLICKER) { - cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; - } else { - cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; - } - - if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES, - TRANSFER_WRITE, cam_reg))) - return err; - - if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, - TRANSFER_WRITE, 1))) - return err; - - switch(mode) { - case NEVER_FLICKER: - case FLICKER_60: - case FLICKER_50: - cam->params.flicker_control.flicker_mode_req = mode; - break; - default: - err = -EINVAL; - } - - return err; -} - -/****************************************************************************** - * - * cpia2_set_property_flip - * - *****************************************************************************/ -void cpia2_set_property_flip(struct camera_data *cam, int prop_val) -{ - unsigned char cam_reg; - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cam_reg = cam->params.vp_params.user_effects; - - if (prop_val) - { - cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP; - } - else - { - cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; - } - cam->params.vp_params.user_effects = cam_reg; - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam_reg); -} - -/****************************************************************************** - * - * cpia2_set_property_mirror - * - *****************************************************************************/ -void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) -{ - unsigned char cam_reg; - - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); - cam_reg = cam->params.vp_params.user_effects; - - if (prop_val) - { - cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR; - } - else - { - cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; - } - cam->params.vp_params.user_effects = cam_reg; - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam_reg); -} - -/****************************************************************************** - * - * cpia2_set_gpio - * - *****************************************************************************/ -int cpia2_set_gpio(struct camera_data *cam, unsigned char setting) -{ - int ret; - - /* Set the microport direction (register 0x90, should be defined - * already) to 1 (user output), and set the microport data (0x91) to - * the value in the ioctl argument. - */ - - ret = cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - CPIA2_VC_MP_DIR_OUTPUT, - 255); - if (ret < 0) - return ret; - cam->params.vp_params.gpio_direction = 255; - - ret = cpia2_do_command(cam, - CPIA2_CMD_SET_VC_MP_GPIO_DATA, - CPIA2_VC_MP_DIR_OUTPUT, - setting); - if (ret < 0) - return ret; - cam->params.vp_params.gpio_data = setting; - - return 0; -} - -/****************************************************************************** - * - * cpia2_set_fps - * - *****************************************************************************/ -int cpia2_set_fps(struct camera_data *cam, int framerate) -{ - int retval; - - switch(framerate) { - case CPIA2_VP_FRAMERATE_30: - case CPIA2_VP_FRAMERATE_25: - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == - CPIA2_VP_SENSOR_FLAGS_500) { - return -EINVAL; - } - fallthrough; - case CPIA2_VP_FRAMERATE_15: - case CPIA2_VP_FRAMERATE_12_5: - case CPIA2_VP_FRAMERATE_7_5: - case CPIA2_VP_FRAMERATE_6_25: - break; - default: - return -EINVAL; - } - - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - framerate == CPIA2_VP_FRAMERATE_15) - framerate = 0; /* Work around bug in VP4 */ - - retval = cpia2_do_command(cam, - CPIA2_CMD_FRAMERATE_REQ, - TRANSFER_WRITE, - framerate); - - if(retval == 0) - cam->params.vp_params.frame_rate = framerate; - - return retval; -} - -/****************************************************************************** - * - * cpia2_set_brightness - * - *****************************************************************************/ -void cpia2_set_brightness(struct camera_data *cam, unsigned char value) -{ - /*** - * Don't let the register be set to zero - bug in VP4 - flash of full - * brightness - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) - value++; - DBG("Setting brightness to %d (0x%0x)\n", value, value); - cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); -} - -/****************************************************************************** - * - * cpia2_set_contrast - * - *****************************************************************************/ -void cpia2_set_contrast(struct camera_data *cam, unsigned char value) -{ - DBG("Setting contrast to %d (0x%0x)\n", value, value); - cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); -} - -/****************************************************************************** - * - * cpia2_set_saturation - * - *****************************************************************************/ -void cpia2_set_saturation(struct camera_data *cam, unsigned char value) -{ - DBG("Setting saturation to %d (0x%0x)\n", value, value); - cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); -} - -/****************************************************************************** - * - * wake_system - * - *****************************************************************************/ -static void wake_system(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); -} - -/****************************************************************************** - * - * set_lowlight_boost - * - * Valid for STV500 sensor only - *****************************************************************************/ -static void set_lowlight_boost(struct camera_data *cam) -{ - struct cpia2_command cmd; - - if (cam->params.pnp_id.device_type != DEVICE_STV_672 || - cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500) - return; - - cmd.direction = TRANSFER_WRITE; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 3; - cmd.start = CPIA2_VP_RAM_ADDR_H; - - cmd.buffer.block_data[0] = 0; /* High byte of address to write to */ - cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */ - cmd.buffer.block_data[2] = 0; /* High byte of data to write */ - - cpia2_send_command(cam, &cmd); - - if (cam->params.vp_params.lowlight_boost) { - cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */ - } else { - cmd.buffer.block_data[0] = 0x06; - } - cmd.start = CPIA2_VP_RAM_DATA; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - - /* Rehash the VP4 values */ - cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1); -} - -/****************************************************************************** - * - * cpia2_set_format - * - * Assumes that new size is already set in param struct. - *****************************************************************************/ -void cpia2_set_format(struct camera_data *cam) -{ - cam->flush = true; - - cpia2_usb_stream_pause(cam); - - /* reset camera to new size */ - cpia2_set_low_power(cam); - cpia2_reset_camera(cam); - cam->flush = false; - - cpia2_dbg_dump_registers(cam); - - cpia2_usb_stream_resume(cam); -} - -/****************************************************************************** - * - * cpia2_dbg_dump_registers - * - *****************************************************************************/ -void cpia2_dbg_dump_registers(struct camera_data *cam) -{ -#ifdef _CPIA2_DEBUG_ - struct cpia2_command cmd; - - if (!(debugs_on & DEBUG_DUMP_REGS)) - return; - - cmd.direction = TRANSFER_READ; - - /* Start with bank 0 (SYSTEM) */ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; - cmd.reg_count = 3; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "System Device Hi = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "System Device Lo = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "System_system control = 0x%X\n", - cmd.buffer.block_data[2]); - - /* Bank 1 (VC) */ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.reg_count = 4; - cmd.start = 0x80; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "ASIC_ID = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "ASIC_REV = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "PW_CONTRL = 0x%X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "WAKEUP = 0x%X\n", - cmd.buffer.block_data[3]); - - cmd.start = 0xA0; /* ST_CTRL */ - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "Stream ctrl = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xA4; /* Stream status */ - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "Stream status = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xA8; /* USB status */ - cmd.reg_count = 3; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "USB_CTRL = 0x%X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "USB_STRM = 0x%X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "USB_STATUS = 0x%X\n", - cmd.buffer.block_data[2]); - - cmd.start = 0xAF; /* USB settings */ - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "USB settings = 0x%X\n", - cmd.buffer.block_data[0]); - - cmd.start = 0xC0; /* VC stuff */ - cmd.reg_count = 26; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VC Control = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VC Format = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VC Clocks = 0x%0X\n", - cmd.buffer.block_data[4]); - printk(KERN_DEBUG "VC IHSize = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n", - cmd.buffer.block_data[8]); - printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n", - cmd.buffer.block_data[9]); - printk(KERN_DEBUG "VC OHSize = 0x%0X\n", - cmd.buffer.block_data[10]); - printk(KERN_DEBUG "VC OVSize = 0x%0X\n", - cmd.buffer.block_data[11]); - printk(KERN_DEBUG "VC HCrop = 0x%0X\n", - cmd.buffer.block_data[12]); - printk(KERN_DEBUG "VC VCrop = 0x%0X\n", - cmd.buffer.block_data[13]); - printk(KERN_DEBUG "VC HPhase = 0x%0X\n", - cmd.buffer.block_data[14]); - printk(KERN_DEBUG "VC VPhase = 0x%0X\n", - cmd.buffer.block_data[15]); - printk(KERN_DEBUG "VC HIspan = 0x%0X\n", - cmd.buffer.block_data[16]); - printk(KERN_DEBUG "VC VIspan = 0x%0X\n", - cmd.buffer.block_data[17]); - printk(KERN_DEBUG "VC HiCrop = 0x%0X\n", - cmd.buffer.block_data[18]); - printk(KERN_DEBUG "VC ViCrop = 0x%0X\n", - cmd.buffer.block_data[19]); - printk(KERN_DEBUG "VC HiFract = 0x%0X\n", - cmd.buffer.block_data[20]); - printk(KERN_DEBUG "VC ViFract = 0x%0X\n", - cmd.buffer.block_data[21]); - printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n", - cmd.buffer.block_data[22]); - printk(KERN_DEBUG "VC Creep Per = 0x%0X\n", - cmd.buffer.block_data[23]); - printk(KERN_DEBUG "VC User Sq. = 0x%0X\n", - cmd.buffer.block_data[24]); - printk(KERN_DEBUG "VC Target KB = 0x%0X\n", - cmd.buffer.block_data[25]); - - /*** VP ***/ - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; - cmd.reg_count = 14; - cmd.start = 0; - cpia2_send_command(cam, &cmd); - - printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Sys State = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP Dev Config = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n", - cmd.buffer.block_data[8]); - printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n", - cmd.buffer.block_data[9]); - printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n", - cmd.buffer.block_data[10]); - printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n", - cmd.buffer.block_data[11]); - printk(KERN_DEBUG "VP RAM Data = 0x%0X\n", - cmd.buffer.block_data[12]); - printk(KERN_DEBUG "Do Call = 0x%0X\n", - cmd.buffer.block_data[13]); - - if (cam->params.pnp_id.device_type == DEVICE_STV_672) { - cmd.reg_count = 9; - cmd.start = 0x0E; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP Framerate = 0x%0X\n", - cmd.buffer.block_data[3]); - printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", - cmd.buffer.block_data[4]); - printk(KERN_DEBUG "VP White Bal = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP WB thresh = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n", - cmd.buffer.block_data[7]); - printk(KERN_DEBUG "VP Exp Target = 0x%0X\n", - cmd.buffer.block_data[8]); - - cmd.reg_count = 1; - cmd.start = 0x1B; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n", - cmd.buffer.block_data[0]); - } else { - cmd.reg_count = 8 ; - cmd.start = 0x0E; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", - cmd.buffer.block_data[5]); - printk(KERN_DEBUG "VP Framerate = 0x%0X\n", - cmd.buffer.block_data[6]); - printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", - cmd.buffer.block_data[7]); - - cmd.reg_count = 1; - cmd.start = CPIA2_VP5_EXPOSURE_TARGET; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n", - cmd.buffer.block_data[0]); - - cmd.reg_count = 4; - cmd.start = 0x3A; - cpia2_send_command(cam, &cmd); - printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n", - cmd.buffer.block_data[0]); - printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n", - cmd.buffer.block_data[1]); - printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n", - cmd.buffer.block_data[2]); - printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n", - cmd.buffer.block_data[3]); - } -#endif -} - -/****************************************************************************** - * - * reset_camera_struct - * - * Sets all values to the defaults - *****************************************************************************/ -static void reset_camera_struct(struct camera_data *cam) -{ - /*** - * The following parameter values are the defaults from the register map. - ***/ - cam->params.vp_params.lowlight_boost = 0; - - /* FlickerModes */ - cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; - - /* jpeg params */ - cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; - cam->params.compression.creep_period = 2; - cam->params.compression.user_squeeze = 20; - cam->params.compression.inhibit_htables = false; - - /* gpio params */ - cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */ - cam->params.vp_params.gpio_data = 0; - - /* Target kb params */ - cam->params.vc_params.quality = 100; - - /*** - * Set Sensor FPS as fast as possible. - ***/ - if(cam->params.pnp_id.device_type == DEVICE_STV_672) { - if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15; - else - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; - } else { - cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; - } - - /*** - * Set default video mode as large as possible : - * for vga sensor set to vga, for cif sensor set to CIF. - ***/ - if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) { - cam->sensor_type = CPIA2_SENSOR_500; - cam->video_size = VIDEOSIZE_VGA; - cam->params.roi.width = STV_IMAGE_VGA_COLS; - cam->params.roi.height = STV_IMAGE_VGA_ROWS; - } else { - cam->sensor_type = CPIA2_SENSOR_410; - cam->video_size = VIDEOSIZE_CIF; - cam->params.roi.width = STV_IMAGE_CIF_COLS; - cam->params.roi.height = STV_IMAGE_CIF_ROWS; - } - - cam->width = cam->params.roi.width; - cam->height = cam->params.roi.height; -} - -/****************************************************************************** - * - * cpia2_init_camera_struct - * - * Deinitialize camera struct - *****************************************************************************/ -void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf) -{ - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); -} - -/****************************************************************************** - * - * cpia2_init_camera_struct - * - * Initializes camera struct, does not call reset to fill in defaults. - *****************************************************************************/ -struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) -{ - struct camera_data *cam; - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - - if (!cam) { - ERR("couldn't kmalloc cpia2 struct\n"); - return NULL; - } - - cam->v4l2_dev.release = cpia2_camera_release; - if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { - v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); - kfree(cam); - return NULL; - } - - mutex_init(&cam->v4l2_lock); - init_waitqueue_head(&cam->wq_stream); - - return cam; -} - -/****************************************************************************** - * - * cpia2_init_camera - * - * Initializes camera. - *****************************************************************************/ -int cpia2_init_camera(struct camera_data *cam) -{ - DBG("Start\n"); - - cam->mmapped = false; - - /* Get sensor and asic types before reset. */ - cpia2_set_high_power(cam); - cpia2_get_version_info(cam); - if (cam->params.version.asic_id != CPIA2_ASIC_672) { - ERR("Device IO error (asicID has incorrect value of 0x%X\n", - cam->params.version.asic_id); - return -ENODEV; - } - - /* Set GPIO direction and data to a safe state. */ - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, - TRANSFER_WRITE, 0); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, - TRANSFER_WRITE, 0); - - /* resetting struct requires version info for sensor and asic types */ - reset_camera_struct(cam); - - cpia2_set_low_power(cam); - - DBG("End\n"); - - return 0; -} - -/****************************************************************************** - * - * cpia2_allocate_buffers - * - *****************************************************************************/ -int cpia2_allocate_buffers(struct camera_data *cam) -{ - int i; - - if(!cam->buffers) { - u32 size = cam->num_frames*sizeof(struct framebuf); - cam->buffers = kmalloc(size, GFP_KERNEL); - if(!cam->buffers) { - ERR("couldn't kmalloc frame buffer structures\n"); - return -ENOMEM; - } - } - - if(!cam->frame_buffer) { - cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames); - if (!cam->frame_buffer) { - ERR("couldn't vmalloc frame buffer data area\n"); - kfree(cam->buffers); - cam->buffers = NULL; - return -ENOMEM; - } - } - - for(i=0; inum_frames-1; ++i) { - cam->buffers[i].next = &cam->buffers[i+1]; - cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - cam->buffers[i].max_length = 0; - cam->buffers[i].num = i; - } - cam->buffers[i].next = cam->buffers; - cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - cam->buffers[i].max_length = 0; - cam->buffers[i].num = i; - cam->curbuff = cam->buffers; - cam->workbuff = cam->curbuff->next; - DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff, - cam->workbuff); - return 0; -} - -/****************************************************************************** - * - * cpia2_free_buffers - * - *****************************************************************************/ -void cpia2_free_buffers(struct camera_data *cam) -{ - if(cam->buffers) { - kfree(cam->buffers); - cam->buffers = NULL; - } - if(cam->frame_buffer) { - rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames); - cam->frame_buffer = NULL; - } -} - -/****************************************************************************** - * - * cpia2_read - * - *****************************************************************************/ -long cpia2_read(struct camera_data *cam, - char __user *buf, unsigned long count, int noblock) -{ - struct framebuf *frame; - - if (!count) - return 0; - - if (!buf) { - ERR("%s: buffer NULL\n",__func__); - return -EINVAL; - } - - if (!cam) { - ERR("%s: Internal error, camera_data NULL!\n",__func__); - return -EINVAL; - } - - if (!cam->streaming) { - /* Start streaming */ - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - - /* Copy cam->curbuff in case it changes while we're processing */ - frame = cam->curbuff; - if (noblock && frame->status != FRAME_READY) { - return -EAGAIN; - } - - if (frame->status != FRAME_READY) { - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !video_is_registered(&cam->vdev) || - (frame = cam->curbuff)->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return 0; - } - - /* copy data to user space */ - if (frame->length > count) - return -EFAULT; - if (copy_to_user(buf, frame->data, frame->length)) - return -EFAULT; - - count = frame->length; - - frame->status = FRAME_EMPTY; - - return count; -} - -/****************************************************************************** - * - * cpia2_poll - * - *****************************************************************************/ -__poll_t cpia2_poll(struct camera_data *cam, struct file *filp, - poll_table *wait) -{ - __poll_t status = v4l2_ctrl_poll(filp, wait); - - if ((poll_requested_events(wait) & (EPOLLIN | EPOLLRDNORM)) && - !cam->streaming) { - /* Start streaming */ - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - - poll_wait(filp, &cam->wq_stream, wait); - - if (cam->curbuff->status == FRAME_READY) - status |= EPOLLIN | EPOLLRDNORM; - - return status; -} - -/****************************************************************************** - * - * cpia2_remap_buffer - * - *****************************************************************************/ -int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) -{ - const char *adr = (const char *)vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long start = (unsigned long) adr; - unsigned long page, pos; - - DBG("mmap offset:%ld size:%ld\n", start_offset, size); - - if (!video_is_registered(&cam->vdev)) - return -ENODEV; - - if (size > cam->frame_size*cam->num_frames || - (start_offset % cam->frame_size) != 0 || - (start_offset+size > cam->frame_size*cam->num_frames)) - return -EINVAL; - - pos = ((unsigned long) (cam->frame_buffer)) + start_offset; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - cam->mmapped = true; - return 0; -} diff --git a/drivers/media/usb/cpia2/cpia2_registers.h b/drivers/media/usb/cpia2/cpia2_registers.h deleted file mode 100644 index 8c73812a15c9..000000000000 --- a/drivers/media/usb/cpia2/cpia2_registers.h +++ /dev/null @@ -1,463 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/**************************************************************************** - * - * Filename: cpia2registers.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Description: - * Definitions for the CPia2 register set - * - ****************************************************************************/ - -#ifndef CPIA2_REGISTER_HEADER -#define CPIA2_REGISTER_HEADER - -/*** - * System register set (Bank 0) - ***/ -#define CPIA2_SYSTEM_DEVICE_HI 0x00 -#define CPIA2_SYSTEM_DEVICE_LO 0x01 - -#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02 -#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00 -#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01 -#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02 -#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10 -#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10 -#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80 - -#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02 -#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04 - -#define CPIA2_SYSTEM_CACHE_CTRL 0x05 -#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01 -#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02 - -#define CPIA2_SYSTEM_SERIAL_CTRL 0x06 -#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00 -#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01 -#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02 -#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03 -#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04 -#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05 - -#define CPIA2_SYSTEM_SERIAL_DATA 0x07 - -#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08 - -/*** - * I2C addresses for various devices in CPiA2 - ***/ -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20 -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88 -#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A - -#define CPIA2_SYSTEM_SPARE_REG1 0x09 -#define CPIA2_SYSTEM_SPARE_REG2 0x0A -#define CPIA2_SYSTEM_SPARE_REG3 0x0B - -#define CPIA2_SYSTEM_MC_PORT_0 0x0C -#define CPIA2_SYSTEM_MC_PORT_1 0x0D -#define CPIA2_SYSTEM_MC_PORT_2 0x0E -#define CPIA2_SYSTEM_MC_PORT_3 0x0F - -#define CPIA2_SYSTEM_STATUS_PKT 0x20 -#define CPIA2_SYSTEM_STATUS_PKT_END 0x27 - -#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30 -#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31 -#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32 -#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33 - -#define CPIA2_SYSTEM_FW_VERSION_HI 0x34 -#define CPIA2_SYSTEM_FW_VERSION_LO 0x35 - -#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80 -#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10 - -/*** - * VC register set (Bank 1) - ***/ -#define CPIA2_VC_ASIC_ID 0x80 - -#define CPIA2_VC_ASIC_REV 0x81 - -#define CPIA2_VC_PW_CTRL 0x82 -#define CPIA2_VC_PW_CTRL_COLDSTART 0x01 -#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02 -#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04 -#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08 -#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10 -#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20 -#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40 -#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80 - -#define CPIA2_VC_WAKEUP 0x83 -#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01 -#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02 -#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04 -#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08 - -#define CPIA2_VC_CLOCK_CTRL 0x84 -#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01 - -#define CPIA2_VC_INT_ENABLE 0x88 -#define CPIA2_VC_INT_ENABLE_XX_IE 0x01 -#define CPIA2_VC_INT_ENABLE_SW_IE 0x02 -#define CPIA2_VC_INT_ENABLE_VC_IE 0x04 -#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08 -#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10 -#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20 - -#define CPIA2_VC_INT_FLAG 0x89 -#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01 -#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02 -#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04 -#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08 -#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10 -#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20 -#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80 - -#define CPIA2_VC_INT_STATE 0x8A -#define CPIA2_VC_INT_STATE_XX_STATE 0x01 -#define CPIA2_VC_INT_STATE_SW_STATE 0x02 - -#define CPIA2_VC_MP_DIR 0x90 -#define CPIA2_VC_MP_DIR_INPUT 0x00 -#define CPIA2_VC_MP_DIR_OUTPUT 0x01 - -#define CPIA2_VC_MP_DATA 0x91 - -#define CPIA2_VC_DP_CTRL 0x98 -#define CPIA2_VC_DP_CTRL_MODE_0 0x00 -#define CPIA2_VC_DP_CTRL_MODE_A 0x01 -#define CPIA2_VC_DP_CTRL_MODE_B 0x02 -#define CPIA2_VC_DP_CTRL_MODE_C 0x03 -#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04 - -#define CPIA2_VC_AD_CTRL 0x99 -#define CPIA2_VC_AD_CTRL_SRC_0 0x00 -#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01 -#define CPIA2_VC_AD_CTRL_SRC_REG 0x02 -#define CPIA2_VC_AD_CTRL_DST_USB 0x00 -#define CPIA2_VC_AD_CTRL_DST_REG 0x04 - -#define CPIA2_VC_AD_TEST_IN 0x9B - -#define CPIA2_VC_AD_TEST_OUT 0x9C - -#define CPIA2_VC_AD_STATUS 0x9D -#define CPIA2_VC_AD_STATUS_EMPTY 0x01 -#define CPIA2_VC_AD_STATUS_FULL 0x02 - -#define CPIA2_VC_DP_DATA 0x9E - -#define CPIA2_VC_ST_CTRL 0xA0 -#define CPIA2_VC_ST_CTRL_SRC_VC 0x00 -#define CPIA2_VC_ST_CTRL_SRC_DP 0x01 -#define CPIA2_VC_ST_CTRL_SRC_REG 0x02 - -#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04 - -#define CPIA2_VC_ST_CTRL_DST_USB 0x00 -#define CPIA2_VC_ST_CTRL_DST_DP 0x08 -#define CPIA2_VC_ST_CTRL_DST_REG 0x10 - -#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20 -#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40 - -#define CPIA2_VC_ST_TEST 0xA1 -#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00 -#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02 - -#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08 - -#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10 - -#define CPIA2_VC_ST_TEST_IN 0xA2 - -#define CPIA2_VC_ST_TEST_OUT 0xA3 - -#define CPIA2_VC_ST_STATUS 0xA4 -#define CPIA2_VC_ST_STATUS_EMPTY 0x01 -#define CPIA2_VC_ST_STATUS_FULL 0x02 - -#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5 - -#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6 - -#define CPIA2_VC_USB_CTRL 0xA8 -#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01 -#define CPIA2_VC_USB_CTRL_CMD_READY 0x02 -#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04 -#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08 -#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10 -#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80 - -#define CPIA2_VC_USB_STRM 0xA9 -#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01 -#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02 -#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04 -#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08 - -#define CPIA2_VC_USB_STATUS 0xAA -#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01 -#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02 -#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04 -#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08 -#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10 -#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20 -#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40 -#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80 - -#define CPIA2_VC_USB_CMDW 0xAB - -#define CPIA2_VC_USB_DATARW 0xAC - -#define CPIA2_VC_USB_INFO 0xAD - -#define CPIA2_VC_USB_CONFIG 0xAE - -#define CPIA2_VC_USB_SETTINGS 0xAF -#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03 -#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C -#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70 - -#define CPIA2_VC_USB_ISOLIM 0xB0 - -#define CPIA2_VC_USB_ISOFAILS 0xB1 - -#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2 - -#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3 - -#define CPIA2_VC_V2W_CTRL 0xB8 -#define CPIA2_VC_V2W_SELECT 0x01 - -#define CPIA2_VC_V2W_SCL 0xB9 - -#define CPIA2_VC_V2W_SDA 0xBA - -#define CPIA2_VC_VC_CTRL 0xC0 -#define CPIA2_VC_VC_CTRL_RUN 0x01 -#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02 -#define CPIA2_VC_VC_CTRL_IDLING 0x04 -#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10 -#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20 -#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40 - -#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1 - -#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2 - -#define CPIA2_VC_VC_FORMAT 0xC3 -#define CPIA2_VC_VC_FORMAT_UFIRST 0x01 -#define CPIA2_VC_VC_FORMAT_MONO 0x02 -#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04 -#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08 -#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10 - -#define CPIA2_VC_VC_CLOCKS 0xC4 -#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03 -#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04 -#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08 -#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00 -#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01 -#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02 -#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03 -#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08 -#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10 - -#define CPIA2_VC_VC_IHSIZE_LO 0xC5 - -#define CPIA2_VC_VC_XLIM_HI 0xC6 - -#define CPIA2_VC_VC_XLIM_LO 0xC7 - -#define CPIA2_VC_VC_YLIM_HI 0xC8 - -#define CPIA2_VC_VC_YLIM_LO 0xC9 - -#define CPIA2_VC_VC_OHSIZE 0xCA - -#define CPIA2_VC_VC_OVSIZE 0xCB - -#define CPIA2_VC_VC_HCROP 0xCC - -#define CPIA2_VC_VC_VCROP 0xCD - -#define CPIA2_VC_VC_HPHASE 0xCE - -#define CPIA2_VC_VC_VPHASE 0xCF - -#define CPIA2_VC_VC_HISPAN 0xD0 - -#define CPIA2_VC_VC_VISPAN 0xD1 - -#define CPIA2_VC_VC_HICROP 0xD2 - -#define CPIA2_VC_VC_VICROP 0xD3 - -#define CPIA2_VC_VC_HFRACT 0xD4 -#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F -#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0 - -#define CPIA2_VC_VC_VFRACT 0xD5 -#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F -#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0 - -#define CPIA2_VC_VC_JPEG_OPT 0xD6 -#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01 -#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02 -#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04 -#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\ - CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE) - - -#define CPIA2_VC_VC_CREEP_PERIOD 0xD7 -#define CPIA2_VC_VC_USER_SQUEEZE 0xD8 -#define CPIA2_VC_VC_TARGET_KB 0xD9 - -#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6 - - -/*** - * VP register set (Bank 2) - ***/ -#define CPIA2_VP_DEVICEH 0 -#define CPIA2_VP_DEVICEL 1 - -#define CPIA2_VP_SYSTEMSTATE 0x02 -#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01 - -#define CPIA2_VP_SYSTEMCTRL 0x03 -#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80 -#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20 -#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10 -#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08 -#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04 -#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02 -#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01 - -#define CPIA2_VP_SENSOR_FLAGS 0x05 -#define CPIA2_VP_SENSOR_FLAGS_404 0x01 -#define CPIA2_VP_SENSOR_FLAGS_407 0x02 -#define CPIA2_VP_SENSOR_FLAGS_409 0x04 -#define CPIA2_VP_SENSOR_FLAGS_410 0x08 -#define CPIA2_VP_SENSOR_FLAGS_500 0x10 - -#define CPIA2_VP_SENSOR_REV 0x06 - -#define CPIA2_VP_DEVICE_CONFIG 0x07 -#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01 - -#define CPIA2_VP_GPIO_DIRECTION 0x08 -#define CPIA2_VP_GPIO_READ 0xFF -#define CPIA2_VP_GPIO_WRITE 0x00 - -#define CPIA2_VP_GPIO_DATA 0x09 - -#define CPIA2_VP_RAM_ADDR_H 0x0A -#define CPIA2_VP_RAM_ADDR_L 0x0B -#define CPIA2_VP_RAM_DATA 0x0C - -#define CPIA2_VP_PATCH_REV 0x0F - -#define CPIA2_VP4_USER_MODE 0x10 -#define CPIA2_VP5_USER_MODE 0x13 -#define CPIA2_VP_USER_MODE_CIF 0x01 -#define CPIA2_VP_USER_MODE_QCIFDS 0x02 -#define CPIA2_VP_USER_MODE_QCIFPTC 0x04 -#define CPIA2_VP_USER_MODE_QVGADS 0x08 -#define CPIA2_VP_USER_MODE_QVGAPTC 0x10 -#define CPIA2_VP_USER_MODE_VGA 0x20 - -#define CPIA2_VP4_FRAMERATE_REQUEST 0x11 -#define CPIA2_VP5_FRAMERATE_REQUEST 0x14 -#define CPIA2_VP_FRAMERATE_60 0x80 -#define CPIA2_VP_FRAMERATE_50 0x40 -#define CPIA2_VP_FRAMERATE_30 0x20 -#define CPIA2_VP_FRAMERATE_25 0x10 -#define CPIA2_VP_FRAMERATE_15 0x08 -#define CPIA2_VP_FRAMERATE_12_5 0x04 -#define CPIA2_VP_FRAMERATE_7_5 0x02 -#define CPIA2_VP_FRAMERATE_6_25 0x01 - -#define CPIA2_VP4_USER_EFFECTS 0x12 -#define CPIA2_VP5_USER_EFFECTS 0x15 -#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01 -#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02 -#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04 -#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only - -/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User - * Effects */ -#define CPIA2_VP_EXPOSURE_MODES 0x15 -#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20 -#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10 - -#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4 -#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5 - -#define CPIA2_VP_FLICKER_MODES 0x1B -#define CPIA2_VP_FLICKER_MODES_50HZ 0x80 -#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40 -#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20 -#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10 -#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08 -#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04 - -#define CPIA2_VP_UMISC 0x1D -#define CPIA2_VP_UMISC_FORCE_MONO 0x80 -#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08 -#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04 -#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02 - -#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34 - -#define CPIA2_VP_INTERPOLATION 0x24 -#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40 -#define CPIA2_VP_INTERPOLATION_HJOG 0x20 -#define CPIA2_VP_INTERPOLATION_VJOG 0x10 - -#define CPIA2_VP_GAMMA 0x25 -#define CPIA2_VP_DEFAULT_GAMMA 0x10 - -#define CPIA2_VP_YRANGE 0x26 - -#define CPIA2_VP_SATURATION 0x27 - -#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58 -#define CPIA2_VP5_MCYRANGE 0x3B //59 -#define CPIA2_VP5_MYCEILING 0x3C //60 -#define CPIA2_VP5_MCUVSATURATION 0x3D //61 - - -#define CPIA2_VP_REHASH_VALUES 0x60 - - -/*** - * Common sensor registers - ***/ -#define CPIA2_SENSOR_DEVICE_H 0x00 -#define CPIA2_SENSOR_DEVICE_L 0x01 - -#define CPIA2_SENSOR_DATA_FORMAT 0x16 -#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08 -#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10 - -#define CPIA2_SENSOR_CR1 0x76 -#define CPIA2_SENSOR_CR1_STAND_BY 0x01 -#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02 -#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04 -#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08 -#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10 -#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20 -#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40 - -#endif diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c deleted file mode 100644 index cba03b286473..000000000000 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ /dev/null @@ -1,966 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/**************************************************************************** - * - * Filename: cpia2_usb.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - ****************************************************************************/ - -#include -#include -#include -#include - -#include "cpia2.h" - -static int frame_sizes[] = { - 0, // USBIF_CMDONLY - 0, // USBIF_BULK - 128, // USBIF_ISO_1 - 384, // USBIF_ISO_2 - 640, // USBIF_ISO_3 - 768, // USBIF_ISO_4 - 896, // USBIF_ISO_5 - 1023, // USBIF_ISO_6 -}; - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt] - -static void process_frame(struct camera_data *cam); -static void cpia2_usb_complete(struct urb *urb); -static int cpia2_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void cpia2_usb_disconnect(struct usb_interface *intf); -static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); -static int cpia2_usb_resume(struct usb_interface *intf); - -static void free_sbufs(struct camera_data *cam); -static void add_APPn(struct camera_data *cam); -static void add_COM(struct camera_data *cam); -static int submit_urbs(struct camera_data *cam); -static int set_alternate(struct camera_data *cam, unsigned int alt); -static int configure_transfer_mode(struct camera_data *cam, unsigned int alt); - -static const struct usb_device_id cpia2_id_table[] = { - {USB_DEVICE(0x0553, 0x0100)}, - {USB_DEVICE(0x0553, 0x0140)}, - {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */ - {} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, cpia2_id_table); - -static struct usb_driver cpia2_driver = { - .name = "cpia2", - .probe = cpia2_usb_probe, - .disconnect = cpia2_usb_disconnect, - .suspend = cpia2_usb_suspend, - .resume = cpia2_usb_resume, - .reset_resume = cpia2_usb_resume, - .id_table = cpia2_id_table -}; - - -/****************************************************************************** - * - * process_frame - * - *****************************************************************************/ -static void process_frame(struct camera_data *cam) -{ - static int frame_count; - - unsigned char *inbuff = cam->workbuff->data; - - DBG("Processing frame #%d, current:%d\n", - cam->workbuff->num, cam->curbuff->num); - - if(cam->workbuff->length > cam->workbuff->max_length) - cam->workbuff->max_length = cam->workbuff->length; - - if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) { - frame_count++; - } else { - cam->workbuff->status = FRAME_ERROR; - DBG("Start of frame not found\n"); - return; - } - - /*** - * Now the output buffer should have a JPEG image in it. - ***/ - if(!cam->first_image_seen) { - /* Always skip the first image after streaming - * starts. It is almost certainly corrupt. */ - cam->first_image_seen = 1; - cam->workbuff->status = FRAME_EMPTY; - return; - } - if (cam->workbuff->length > 3) { - if(cam->mmapped && - cam->workbuff->length < cam->workbuff->max_length) { - /* No junk in the buffers */ - memset(cam->workbuff->data+cam->workbuff->length, - 0, cam->workbuff->max_length- - cam->workbuff->length); - } - cam->workbuff->max_length = cam->workbuff->length; - cam->workbuff->status = FRAME_READY; - - if(!cam->mmapped && cam->num_frames > 2) { - /* During normal reading, the most recent - * frame will be read. If the current frame - * hasn't started reading yet, it will never - * be read, so mark it empty. If the buffer is - * mmapped, or we have few buffers, we need to - * wait for the user to free the buffer. - * - * NOTE: This is not entirely foolproof with 3 - * buffers, but it would take an EXTREMELY - * overloaded system to cause problems (possible - * image data corruption). Basically, it would - * need to take more time to execute cpia2_read - * than it would for the camera to send - * cam->num_frames-2 frames before problems - * could occur. - */ - cam->curbuff->status = FRAME_EMPTY; - } - cam->curbuff = cam->workbuff; - cam->workbuff = cam->workbuff->next; - DBG("Changed buffers, work:%d, current:%d\n", - cam->workbuff->num, cam->curbuff->num); - return; - } else { - DBG("Not enough data for an image.\n"); - } - - cam->workbuff->status = FRAME_ERROR; - return; -} - -/****************************************************************************** - * - * add_APPn - * - * Adds a user specified APPn record - *****************************************************************************/ -static void add_APPn(struct camera_data *cam) -{ - if(cam->APP_len > 0) { - cam->workbuff->data[cam->workbuff->length++] = 0xFF; - cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn; - cam->workbuff->data[cam->workbuff->length++] = 0; - cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2; - memcpy(cam->workbuff->data+cam->workbuff->length, - cam->APP_data, cam->APP_len); - cam->workbuff->length += cam->APP_len; - } -} - -/****************************************************************************** - * - * add_COM - * - * Adds a user specified COM record - *****************************************************************************/ -static void add_COM(struct camera_data *cam) -{ - if(cam->COM_len > 0) { - cam->workbuff->data[cam->workbuff->length++] = 0xFF; - cam->workbuff->data[cam->workbuff->length++] = 0xFE; - cam->workbuff->data[cam->workbuff->length++] = 0; - cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2; - memcpy(cam->workbuff->data+cam->workbuff->length, - cam->COM_data, cam->COM_len); - cam->workbuff->length += cam->COM_len; - } -} - -/****************************************************************************** - * - * cpia2_usb_complete - * - * callback when incoming packet is received - *****************************************************************************/ -static void cpia2_usb_complete(struct urb *urb) -{ - int i; - unsigned char *cdata; - static bool frame_ready = false; - struct camera_data *cam = (struct camera_data *) urb->context; - - if (urb->status!=0) { - if (!(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) - { - DBG("urb->status = %d!\n", urb->status); - } - DBG("Stopping streaming\n"); - return; - } - - if (!cam->streaming || !video_is_registered(&cam->vdev)) { - LOG("Will now stop the streaming: streaming = %d, present=%d\n", - cam->streaming, video_is_registered(&cam->vdev)); - return; - } - - /*** - * Packet collater - ***/ - //DBG("Collating %d packets\n", urb->number_of_packets); - for (i = 0; i < urb->number_of_packets; i++) { - u16 checksum, iso_checksum; - int j; - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - if(cam->workbuff->status == FRAME_READY) { - struct framebuf *ptr; - /* Try to find an available buffer */ - DBG("workbuff full, searching\n"); - for (ptr = cam->workbuff->next; - ptr != cam->workbuff; - ptr = ptr->next) - { - if (ptr->status == FRAME_EMPTY) { - ptr->status = FRAME_READING; - ptr->length = 0; - break; - } - } - if (ptr == cam->workbuff) - break; /* No READING or EMPTY buffers left */ - - cam->workbuff = ptr; - } - - if (cam->workbuff->status == FRAME_EMPTY || - cam->workbuff->status == FRAME_ERROR) { - cam->workbuff->status = FRAME_READING; - cam->workbuff->length = 0; - } - - //DBG(" Packet %d length = %d, status = %d\n", i, n, st); - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st) { - LOG("cpia2 data error: [%d] len=%d, status = %d\n", - i, n, st); - if(!ALLOW_CORRUPT) - cam->workbuff->status = FRAME_ERROR; - continue; - } - - if(n<=2) - continue; - - checksum = 0; - for(j=0; jworkbuff->status = FRAME_ERROR; - continue; - } - } - n -= 2; - - if(cam->workbuff->status != FRAME_READING) { - if((0xFF == cdata[0] && 0xD8 == cdata[1]) || - (0xD8 == cdata[0] && 0xFF == cdata[1] && - 0 != cdata[2])) { - /* frame is skipped, but increment total - * frame count anyway */ - cam->frame_count++; - } - DBG("workbuff not reading, status=%d\n", - cam->workbuff->status); - continue; - } - - if (cam->frame_size < cam->workbuff->length + n) { - ERR("buffer overflow! length: %d, n: %d\n", - cam->workbuff->length, n); - cam->workbuff->status = FRAME_ERROR; - if(cam->workbuff->length > cam->workbuff->max_length) - cam->workbuff->max_length = - cam->workbuff->length; - continue; - } - - if (cam->workbuff->length == 0) { - int data_offset; - if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) { - data_offset = 1; - } else if((0xFF == cdata[0]) && (0xD8 == cdata[1]) - && (0xFF == cdata[2])) { - data_offset = 2; - } else { - DBG("Ignoring packet, not beginning!\n"); - continue; - } - DBG("Start of frame pattern found\n"); - cam->workbuff->ts = ktime_get_ns(); - cam->workbuff->seq = cam->frame_count++; - cam->workbuff->data[0] = 0xFF; - cam->workbuff->data[1] = 0xD8; - cam->workbuff->length = 2; - add_APPn(cam); - add_COM(cam); - memcpy(cam->workbuff->data+cam->workbuff->length, - cdata+data_offset, n-data_offset); - cam->workbuff->length += n-data_offset; - } else if (cam->workbuff->length > 0) { - memcpy(cam->workbuff->data + cam->workbuff->length, - cdata, n); - cam->workbuff->length += n; - } - - if ((cam->workbuff->length >= 3) && - (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) && - (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) && - (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) { - frame_ready = true; - cam->workbuff->data[cam->workbuff->length - 1] = 0; - cam->workbuff->length -= 1; - } else if ((cam->workbuff->length >= 2) && - (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) && - (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) { - frame_ready = true; - } - - if (frame_ready) { - DBG("Workbuff image size = %d\n",cam->workbuff->length); - process_frame(cam); - - frame_ready = false; - - if (waitqueue_active(&cam->wq_stream)) - wake_up_interruptible(&cam->wq_stream); - } - } - - if(cam->streaming) { - /* resubmit */ - urb->dev = cam->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - ERR("%s: usb_submit_urb ret %d!\n", __func__, i); - } -} - -/****************************************************************************** - * - * configure_transfer_mode - * - *****************************************************************************/ -static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) -{ - static unsigned char iso_regs[8][4] = { - {0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00}, - {0xB9, 0x00, 0x00, 0x7E}, - {0xB9, 0x00, 0x01, 0x7E}, - {0xB9, 0x00, 0x02, 0x7E}, - {0xB9, 0x00, 0x02, 0xFE}, - {0xB9, 0x00, 0x03, 0x7E}, - {0xB9, 0x00, 0x03, 0xFD} - }; - struct cpia2_command cmd; - unsigned char reg; - - if (!video_is_registered(&cam->vdev)) - return -ENODEV; - - /*** - * Write the isoc registers according to the alternate selected - ***/ - cmd.direction = TRANSFER_WRITE; - cmd.buffer.block_data[0] = iso_regs[alt][0]; - cmd.buffer.block_data[1] = iso_regs[alt][1]; - cmd.buffer.block_data[2] = iso_regs[alt][2]; - cmd.buffer.block_data[3] = iso_regs[alt][3]; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_USB_ISOLIM; - cmd.reg_count = 4; - cpia2_send_command(cam, &cmd); - - /*** - * Enable relevant streams before starting polling. - * First read USB Stream Config Register. - ***/ - cmd.direction = TRANSFER_READ; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cmd.start = CPIA2_VC_USB_STRM; - cmd.reg_count = 1; - cpia2_send_command(cam, &cmd); - reg = cmd.buffer.block_data[0]; - - /* Clear iso, bulk, and int */ - reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE | - CPIA2_VC_USB_STRM_ISO_ENABLE | - CPIA2_VC_USB_STRM_INT_ENABLE); - - if (alt == USBIF_BULK) { - DBG("Enabling bulk xfer\n"); - reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */ - cam->xfer_mode = XFER_BULK; - } else if (alt >= USBIF_ISO_1) { - DBG("Enabling ISOC xfer\n"); - reg |= CPIA2_VC_USB_STRM_ISO_ENABLE; - cam->xfer_mode = XFER_ISOC; - } - - cmd.buffer.block_data[0] = reg; - cmd.direction = TRANSFER_WRITE; - cmd.start = CPIA2_VC_USB_STRM; - cmd.reg_count = 1; - cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; - cpia2_send_command(cam, &cmd); - - return 0; -} - -/****************************************************************************** - * - * cpia2_usb_change_streaming_alternate - * - *****************************************************************************/ -int cpia2_usb_change_streaming_alternate(struct camera_data *cam, - unsigned int alt) -{ - int ret = 0; - - if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6) - return -EINVAL; - - if(alt == cam->params.camera_state.stream_mode) - return 0; - - cpia2_usb_stream_pause(cam); - - configure_transfer_mode(cam, alt); - - cam->params.camera_state.stream_mode = alt; - - /* Reset the camera to prevent image quality degradation */ - cpia2_reset_camera(cam); - - cpia2_usb_stream_resume(cam); - - return ret; -} - -/****************************************************************************** - * - * set_alternate - * - *****************************************************************************/ -static int set_alternate(struct camera_data *cam, unsigned int alt) -{ - int ret = 0; - - if(alt == cam->cur_alt) - return 0; - - if (cam->cur_alt != USBIF_CMDONLY) { - DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY); - ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY); - if (ret != 0) - return ret; - } - if (alt != USBIF_CMDONLY) { - DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt); - ret = usb_set_interface(cam->dev, cam->iface, alt); - if (ret != 0) - return ret; - } - - cam->old_alt = cam->cur_alt; - cam->cur_alt = alt; - - return ret; -} - -/****************************************************************************** - * - * free_sbufs - * - * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL - * are assumed to be allocated. Non-NULL .urb members are also assumed to be - * submitted (and must therefore be killed before they are freed). - *****************************************************************************/ -static void free_sbufs(struct camera_data *cam) -{ - int i; - - for (i = 0; i < NUM_SBUF; i++) { - if(cam->sbuf[i].urb) { - usb_kill_urb(cam->sbuf[i].urb); - usb_free_urb(cam->sbuf[i].urb); - cam->sbuf[i].urb = NULL; - } - if(cam->sbuf[i].data) { - kfree(cam->sbuf[i].data); - cam->sbuf[i].data = NULL; - } - } -} - -/******* -* Convenience functions -*******/ -/**************************************************************************** - * - * write_packet - * - ***************************************************************************/ -static int write_packet(struct usb_device *udev, - u8 request, u8 * registers, u16 start, size_t size) -{ - unsigned char *buf; - int ret; - - if (!registers || size <= 0) - return -EINVAL; - - buf = kmemdup(registers, size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - start, /* value */ - 0, /* index */ - buf, /* buffer */ - size, - 1000); - - kfree(buf); - return ret; -} - -/**************************************************************************** - * - * read_packet - * - ***************************************************************************/ -static int read_packet(struct usb_device *udev, - u8 request, u8 * registers, u16 start, size_t size) -{ - unsigned char *buf; - int ret; - - if (!registers || size <= 0) - return -EINVAL; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - request, - USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE, - start, /* value */ - 0, /* index */ - buf, /* buffer */ - size, - 1000); - - if (ret >= 0) - memcpy(registers, buf, size); - - kfree(buf); - - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_transfer_cmd - * - *****************************************************************************/ -int cpia2_usb_transfer_cmd(struct camera_data *cam, - void *registers, - u8 request, u8 start, u8 count, u8 direction) -{ - int err = 0; - struct usb_device *udev = cam->dev; - - if (!udev) { - ERR("%s: Internal driver error: udev is NULL\n", __func__); - return -EINVAL; - } - - if (!registers) { - ERR("%s: Internal driver error: register array is NULL\n", __func__); - return -EINVAL; - } - - if (direction == TRANSFER_READ) { - err = read_packet(udev, request, (u8 *)registers, start, count); - if (err > 0) - err = 0; - } else if (direction == TRANSFER_WRITE) { - err =write_packet(udev, request, (u8 *)registers, start, count); - if (err < 0) { - LOG("Control message failed, err val = %d\n", err); - LOG("Message: request = 0x%0X, start = 0x%0X\n", - request, start); - LOG("Message: count = %d, register[0] = 0x%0X\n", - count, ((unsigned char *) registers)[0]); - } else - err=0; - } else { - LOG("Unexpected first byte of direction: %d\n", - direction); - return -EINVAL; - } - - if(err != 0) - LOG("Unexpected error: %d\n", err); - return err; -} - - -/****************************************************************************** - * - * submit_urbs - * - *****************************************************************************/ -static int submit_urbs(struct camera_data *cam) -{ - struct urb *urb; - int fx, err, i, j; - - for(i=0; isbuf[i].data) - continue; - cam->sbuf[i].data = - kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC, - GFP_KERNEL); - if (!cam->sbuf[i].data) { - while (--i >= 0) { - kfree(cam->sbuf[i].data); - cam->sbuf[i].data = NULL; - } - return -ENOMEM; - } - } - - /* We double buffer the Isoc lists, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - for(i=0; isbuf[i].urb) { - continue; - } - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - for (j = 0; j < i; j++) - usb_free_urb(cam->sbuf[j].urb); - for (j = 0; j < NUM_SBUF; j++) { - kfree(cam->sbuf[j].data); - cam->sbuf[j].data = NULL; - } - return -ENOMEM; - } - - cam->sbuf[i].urb = urb; - urb->dev = cam->dev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = cam->sbuf[i].data; - urb->complete = cpia2_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = - FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = - FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - } - - - /* Queue the ISO urbs, and resubmit in the completion handler */ - for(i=0; isbuf[i].urb, GFP_KERNEL); - if (err) { - ERR("usb_submit_urb[%d]() = %d\n", i, err); - return err; - } - } - - return 0; -} - -/****************************************************************************** - * - * cpia2_usb_stream_start - * - *****************************************************************************/ -int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate) -{ - int ret; - int old_alt; - - if(cam->streaming) - return 0; - - if (cam->flush) { - int i; - DBG("Flushing buffers\n"); - for(i=0; inum_frames; ++i) { - cam->buffers[i].status = FRAME_EMPTY; - cam->buffers[i].length = 0; - } - cam->curbuff = &cam->buffers[0]; - cam->workbuff = cam->curbuff->next; - cam->flush = false; - } - - old_alt = cam->params.camera_state.stream_mode; - cam->params.camera_state.stream_mode = 0; - ret = cpia2_usb_change_streaming_alternate(cam, alternate); - if (ret < 0) { - int ret2; - ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret); - cam->params.camera_state.stream_mode = old_alt; - ret2 = set_alternate(cam, USBIF_CMDONLY); - if (ret2 < 0) { - ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already failed. Then tried to call set_alternate(USBIF_CMDONLY) = %d.\n", - alternate, ret, ret2); - } - } else { - cam->frame_count = 0; - cam->streaming = 1; - ret = cpia2_usb_stream_resume(cam); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_pause - * - *****************************************************************************/ -int cpia2_usb_stream_pause(struct camera_data *cam) -{ - int ret = 0; - if(cam->streaming) { - free_sbufs(cam); - ret = set_alternate(cam, USBIF_CMDONLY); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_resume - * - *****************************************************************************/ -int cpia2_usb_stream_resume(struct camera_data *cam) -{ - int ret = 0; - if(cam->streaming) { - cam->first_image_seen = 0; - ret = set_alternate(cam, cam->params.camera_state.stream_mode); - if(ret == 0) { - /* for some reason the user effects need to be set - again when starting streaming. */ - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam->params.vp_params.user_effects); - ret = submit_urbs(cam); - } - } - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_stream_stop - * - *****************************************************************************/ -int cpia2_usb_stream_stop(struct camera_data *cam) -{ - int ret; - - ret = cpia2_usb_stream_pause(cam); - cam->streaming = 0; - configure_transfer_mode(cam, 0); - return ret; -} - -/****************************************************************************** - * - * cpia2_usb_probe - * - * Probe and initialize. - *****************************************************************************/ -static int cpia2_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_interface_descriptor *interface; - struct camera_data *cam; - int ret; - - /* A multi-config CPiA2 camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - interface = &intf->cur_altsetting->desc; - - /* If we get to this point, we found a CPiA2 camera */ - LOG("CPiA2 USB camera found\n"); - - cam = cpia2_init_camera_struct(intf); - if (cam == NULL) - return -ENOMEM; - - cam->dev = udev; - cam->iface = interface->bInterfaceNumber; - - ret = set_alternate(cam, USBIF_CMDONLY); - if (ret < 0) { - ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret); - goto alt_err; - } - - - if((ret = cpia2_init_camera(cam)) < 0) { - ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); - goto alt_err; - } - LOG(" CPiA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmware_revision_hi, - cam->params.version.firmware_revision_lo, - cam->params.version.asic_id, - cam->params.version.asic_rev); - LOG(" CPiA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnp_id.vendor, - cam->params.pnp_id.product, - cam->params.pnp_id.device_revision); - LOG(" SensorID: %d.(version %d)\n", - cam->params.version.sensor_flags, - cam->params.version.sensor_rev); - - usb_set_intfdata(intf, cam); - - ret = cpia2_register_camera(cam); - if (ret < 0) { - ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); - goto alt_err; - } - - return 0; - -alt_err: - cpia2_deinit_camera_struct(cam, intf); - return ret; -} - -/****************************************************************************** - * - * cpia2_disconnect - * - *****************************************************************************/ -static void cpia2_usb_disconnect(struct usb_interface *intf) -{ - struct camera_data *cam = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - - DBG("Stopping stream\n"); - cpia2_usb_stream_stop(cam); - - mutex_lock(&cam->v4l2_lock); - DBG("Unregistering camera\n"); - cpia2_unregister_camera(cam); - v4l2_device_disconnect(&cam->v4l2_dev); - mutex_unlock(&cam->v4l2_lock); - - if(cam->buffers) { - DBG("Wakeup waiting processes\n"); - cam->curbuff->status = FRAME_READY; - cam->curbuff->length = 0; - wake_up_interruptible(&cam->wq_stream); - } - - v4l2_device_put(&cam->v4l2_dev); - - LOG("CPiA2 camera disconnected.\n"); -} - -static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct camera_data *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->v4l2_lock); - if (cam->streaming) { - cpia2_usb_stream_stop(cam); - cam->streaming = 1; - } - mutex_unlock(&cam->v4l2_lock); - - dev_info(&intf->dev, "going into suspend..\n"); - return 0; -} - -/* Resume device - start device. */ -static int cpia2_usb_resume(struct usb_interface *intf) -{ - struct camera_data *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->v4l2_lock); - v4l2_ctrl_handler_setup(&cam->hdl); - if (cam->streaming) { - cam->streaming = 0; - cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - } - mutex_unlock(&cam->v4l2_lock); - - dev_info(&intf->dev, "coming out of suspend..\n"); - return 0; -} - -/****************************************************************************** - * - * usb_cpia2_init - * - *****************************************************************************/ -int cpia2_usb_init(void) -{ - return usb_register(&cpia2_driver); -} - -/****************************************************************************** - * - * usb_cpia_cleanup - * - *****************************************************************************/ -void cpia2_usb_cleanup(void) -{ - schedule_timeout(2 * HZ); - usb_deregister(&cpia2_driver); -} diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c deleted file mode 100644 index 926ecfc9b64a..000000000000 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ /dev/null @@ -1,1226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/**************************************************************************** - * - * Filename: cpia2_v4l.c - * - * Copyright 2001, STMicrolectronics, Inc. - * Contact: steve.miller@st.com - * Copyright 2001,2005, Scott J. Bertin - * - * Description: - * This is a USB driver for CPia2 based video cameras. - * The infrastructure of this driver is based on the cpia usb driver by - * Jochen Scharrlach and Johannes Erdfeldt. - * - * Stripped of 2.4 stuff ready for main kernel submit by - * Alan Cox - ****************************************************************************/ - -#define CPIA_VERSION "3.0.1" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia2.h" - -static int video_nr = -1; -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); - -static int buffer_size = 68 * 1024; -module_param(buffer_size, int, 0); -MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); - -static int num_buffers = 3; -module_param(num_buffers, int, 0); -MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-" - __stringify(VIDEO_MAX_FRAME) ", default 3)"); - -static int alternate = DEFAULT_ALT; -module_param(alternate, int, 0); -MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" - __stringify(USBIF_ISO_6) ", default " - __stringify(DEFAULT_ALT) ")"); - -static int flicker_mode; -module_param(flicker_mode, int, 0); -MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " - __stringify(60) ", default 0)"); - -MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); -MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(CPIA_VERSION); - -#define ABOUT "V4L-Driver for Vision CPiA2 based cameras" -#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) - -/****************************************************************************** - * - * cpia2_open - * - *****************************************************************************/ -static int cpia2_open(struct file *file) -{ - struct camera_data *cam = video_drvdata(file); - int retval; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - retval = v4l2_fh_open(file); - if (retval) - goto open_unlock; - - if (v4l2_fh_is_singular_file(file)) { - if (cpia2_allocate_buffers(cam)) { - v4l2_fh_release(file); - retval = -ENOMEM; - goto open_unlock; - } - - /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) { - v4l2_fh_release(file); - retval = -EIO; - goto open_unlock; - } - - cam->APP_len = 0; - cam->COM_len = 0; - } - - cpia2_dbg_dump_registers(cam); -open_unlock: - mutex_unlock(&cam->v4l2_lock); - return retval; -} - -/****************************************************************************** - * - * cpia2_close - * - *****************************************************************************/ -static int cpia2_close(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); - - mutex_lock(&cam->v4l2_lock); - if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { - cpia2_usb_stream_stop(cam); - - /* save camera state for later open */ - cpia2_save_camera_state(cam); - - cpia2_set_low_power(cam); - cpia2_free_buffers(cam); - } - - if (cam->stream_fh == file->private_data) { - cam->stream_fh = NULL; - cam->mmapped = 0; - } - mutex_unlock(&cam->v4l2_lock); - return v4l2_fh_release(file); -} - -/****************************************************************************** - * - * cpia2_v4l_read - * - *****************************************************************************/ -static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, - loff_t *off) -{ - struct camera_data *cam = video_drvdata(file); - int noblock = file->f_flags & O_NONBLOCK; - ssize_t ret; - - if (!cam) - return -EINVAL; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - ret = cpia2_read(cam, buf, count, noblock); - mutex_unlock(&cam->v4l2_lock); - return ret; -} - -/****************************************************************************** - * - * cpia2_v4l_poll - * - *****************************************************************************/ -static __poll_t cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) -{ - struct camera_data *cam = video_drvdata(filp); - __poll_t res; - - mutex_lock(&cam->v4l2_lock); - res = cpia2_poll(cam, filp, wait); - mutex_unlock(&cam->v4l2_lock); - return res; -} - -static int sync(struct camera_data *cam, int frame_nr) -{ - struct framebuf *frame = &cam->buffers[frame_nr]; - - while (1) { - if (frame->status == FRAME_READY) - return 0; - - if (!cam->streaming) { - frame->status = FRAME_READY; - frame->length = 0; - return 0; - } - - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !cam->streaming || - frame->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return -ENOTTY; - } -} - -/****************************************************************************** - * - * ioctl_querycap - * - * V4L2 device capabilities - * - *****************************************************************************/ - -static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) -{ - struct camera_data *cam = video_drvdata(file); - - strscpy(vc->driver, "cpia2", sizeof(vc->driver)); - - if (cam->params.pnp_id.product == 0x151) - strscpy(vc->card, "QX5 Microscope", sizeof(vc->card)); - else - strscpy(vc->card, "CPiA2 Camera", sizeof(vc->card)); - switch (cam->params.pnp_id.device_type) { - case DEVICE_STV_672: - strcat(vc->card, " (672/"); - break; - case DEVICE_STV_676: - strcat(vc->card, " (676/"); - break; - default: - strcat(vc->card, " (XXX/"); - break; - } - switch (cam->params.version.sensor_flags) { - case CPIA2_VP_SENSOR_FLAGS_404: - strcat(vc->card, "404)"); - break; - case CPIA2_VP_SENSOR_FLAGS_407: - strcat(vc->card, "407)"); - break; - case CPIA2_VP_SENSOR_FLAGS_409: - strcat(vc->card, "409)"); - break; - case CPIA2_VP_SENSOR_FLAGS_410: - strcat(vc->card, "410)"); - break; - case CPIA2_VP_SENSOR_FLAGS_500: - strcat(vc->card, "500)"); - break; - default: - strcat(vc->card, "XXX)"); - break; - } - - if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) < 0) - memset(vc->bus_info, 0, sizeof(vc->bus_info)); - return 0; -} - -/****************************************************************************** - * - * ioctl_input - * - * V4L2 input get/set/enumerate - * - *****************************************************************************/ - -static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - if (i->index) - return -EINVAL; - strscpy(i->name, "Camera", sizeof(i->name)); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int cpia2_s_input(struct file *file, void *fh, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -/****************************************************************************** - * - * ioctl_enum_fmt - * - * V4L2 format enumerate - * - *****************************************************************************/ - -static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index > 1) - return -EINVAL; - - if (f->index == 0) - f->pixelformat = V4L2_PIX_FMT_MJPEG; - else - f->pixelformat = V4L2_PIX_FMT_JPEG; - return 0; -} - -/****************************************************************************** - * - * ioctl_try_fmt - * - * V4L2 format try - * - *****************************************************************************/ - -static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = cam->frame_size; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - - switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) { - case VIDEOSIZE_VGA: - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - break; - case VIDEOSIZE_CIF: - f->fmt.pix.width = 352; - f->fmt.pix.height = 288; - break; - case VIDEOSIZE_QVGA: - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - break; - case VIDEOSIZE_288_216: - f->fmt.pix.width = 288; - f->fmt.pix.height = 216; - break; - case VIDEOSIZE_256_192: - f->fmt.pix.width = 256; - f->fmt.pix.height = 192; - break; - case VIDEOSIZE_224_168: - f->fmt.pix.width = 224; - f->fmt.pix.height = 168; - break; - case VIDEOSIZE_192_144: - f->fmt.pix.width = 192; - f->fmt.pix.height = 144; - break; - case VIDEOSIZE_QCIF: - default: - f->fmt.pix.width = 176; - f->fmt.pix.height = 144; - break; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_set_fmt - * - * V4L2 format set - * - *****************************************************************************/ - -static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - int err, frame; - - err = cpia2_try_fmt_vid_cap(file, _fh, f); - if (err != 0) - return err; - - cam->pixelformat = f->fmt.pix.pixelformat; - - /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle - * the missing Huffman table properly. - */ - cam->params.compression.inhibit_htables = 0; - /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/ - - /* we set the video window to something smaller or equal to what - * is requested by the user??? - */ - DBG("Requested width = %d, height = %d\n", - f->fmt.pix.width, f->fmt.pix.height); - if (f->fmt.pix.width != cam->width || - f->fmt.pix.height != cam->height) { - cam->width = f->fmt.pix.width; - cam->height = f->fmt.pix.height; - cam->params.roi.width = f->fmt.pix.width; - cam->params.roi.height = f->fmt.pix.height; - cpia2_set_format(cam); - } - - for (frame = 0; frame < cam->num_frames; ++frame) { - if (cam->buffers[frame].status == FRAME_READING) - if ((err = sync(cam, frame)) < 0) - return err; - - cam->buffers[frame].status = FRAME_EMPTY; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_get_fmt - * - * V4L2 format get - * - *****************************************************************************/ - -static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct camera_data *cam = video_drvdata(file); - - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; - f->fmt.pix.pixelformat = cam->pixelformat; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = cam->frame_size; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - - return 0; -} - -/****************************************************************************** - * - * ioctl_cropcap - * - * V4L2 query cropping capabilities - * NOTE: cropping is currently disabled - * - *****************************************************************************/ - -static int cpia2_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct camera_data *cam = video_drvdata(file); - - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - s->r.left = 0; - s->r.top = 0; - s->r.width = cam->width; - s->r.height = cam->height; - break; - default: - return -EINVAL; - } - return 0; -} - -struct framerate_info { - int value; - struct v4l2_fract period; -}; - -static const struct framerate_info framerate_controls[] = { - { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, - { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, - { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, - { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, - { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, - { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, -}; - -static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) -{ - struct camera_data *cam = video_drvdata(file); - struct v4l2_captureparm *cap = &p->parm.capture; - int i; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cap->capability = V4L2_CAP_TIMEPERFRAME; - cap->readbuffers = cam->num_frames; - for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) - if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { - cap->timeperframe = framerate_controls[i].period; - break; - } - return 0; -} - -static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) -{ - struct camera_data *cam = video_drvdata(file); - struct v4l2_captureparm *cap = &p->parm.capture; - struct v4l2_fract tpf = cap->timeperframe; - int max = ARRAY_SIZE(framerate_controls) - 1; - int ret; - int i; - - ret = cpia2_g_parm(file, fh, p); - if (ret || !tpf.denominator || !tpf.numerator) - return ret; - - /* Maximum 15 fps for this model */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - max -= 2; - for (i = 0; i <= max; i++) { - struct v4l2_fract f1 = tpf; - struct v4l2_fract f2 = framerate_controls[i].period; - - f1.numerator *= f2.denominator; - f2.numerator *= f1.denominator; - if (f1.numerator >= f2.numerator) - break; - } - if (i > max) - i = max; - cap->timeperframe = framerate_controls[i].period; - return cpia2_set_fps(cam, framerate_controls[i].value); -} - -static const struct { - u32 width; - u32 height; -} cpia2_framesizes[] = { - { 640, 480 }, - { 352, 288 }, - { 320, 240 }, - { 288, 216 }, - { 256, 192 }, - { 224, 168 }, - { 192, 144 }, - { 176, 144 }, -}; - -static int cpia2_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && - fsize->pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) - return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = cpia2_framesizes[fsize->index].width; - fsize->discrete.height = cpia2_framesizes[fsize->index].height; - - return 0; -} - -static int cpia2_enum_frameintervals(struct file *file, void *fh, - struct v4l2_frmivalenum *fival) -{ - struct camera_data *cam = video_drvdata(file); - int max = ARRAY_SIZE(framerate_controls) - 1; - int i; - - if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && - fival->pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - /* Maximum 15 fps for this model */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) - max -= 2; - if (fival->index > max) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) - if (fival->width == cpia2_framesizes[i].width && - fival->height == cpia2_framesizes[i].height) - break; - if (i == ARRAY_SIZE(cpia2_framesizes)) - return -EINVAL; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = framerate_controls[fival->index].period; - return 0; -} - -/****************************************************************************** - * - * ioctl_s_ctrl - * - * V4L2 set the value of a control variable - * - *****************************************************************************/ - -static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct camera_data *cam = - container_of(ctrl->handler, struct camera_data, hdl); - static const int flicker_table[] = { - NEVER_FLICKER, - FLICKER_50, - FLICKER_60, - }; - - DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cpia2_set_brightness(cam, ctrl->val); - break; - case V4L2_CID_CONTRAST: - cpia2_set_contrast(cam, ctrl->val); - break; - case V4L2_CID_SATURATION: - cpia2_set_saturation(cam, ctrl->val); - break; - case V4L2_CID_HFLIP: - cpia2_set_property_mirror(cam, ctrl->val); - break; - case V4L2_CID_VFLIP: - cpia2_set_property_flip(cam, ctrl->val); - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); - case V4L2_CID_ILLUMINATORS_1: - return cpia2_set_gpio(cam, (cam->top_light->val << 6) | - (cam->bottom_light->val << 7)); - case V4L2_CID_JPEG_ACTIVE_MARKER: - cam->params.compression.inhibit_htables = - !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - cam->params.vc_params.quality = ctrl->val; - break; - case CPIA2_CID_USB_ALT: - cam->params.camera_state.stream_mode = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_g_jpegcomp - * - * V4L2 get the JPEG compression parameters - * - *****************************************************************************/ - -static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) -{ - struct camera_data *cam = video_drvdata(file); - - memset(parms, 0, sizeof(*parms)); - - parms->quality = 80; // TODO: Can this be made meaningful? - - parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; - if (!cam->params.compression.inhibit_htables) - parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT; - - parms->APPn = cam->APPn; - parms->APP_len = cam->APP_len; - if (cam->APP_len > 0) { - memcpy(parms->APP_data, cam->APP_data, cam->APP_len); - parms->jpeg_markers |= V4L2_JPEG_MARKER_APP; - } - - parms->COM_len = cam->COM_len; - if (cam->COM_len > 0) { - memcpy(parms->COM_data, cam->COM_data, cam->COM_len); - parms->jpeg_markers |= JPEG_MARKER_COM; - } - - DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n", - parms->APP_len, parms->COM_len); - - return 0; -} - -/****************************************************************************** - * - * ioctl_s_jpegcomp - * - * V4L2 set the JPEG compression parameters - * NOTE: quality and some jpeg_markers are ignored. - * - *****************************************************************************/ - -static int cpia2_s_jpegcomp(struct file *file, void *fh, - const struct v4l2_jpegcompression *parms) -{ - struct camera_data *cam = video_drvdata(file); - - DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", - parms->APP_len, parms->COM_len); - - cam->params.compression.inhibit_htables = - !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); - - if (parms->APP_len != 0) { - if (parms->APP_len > 0 && - parms->APP_len <= sizeof(cam->APP_data) && - parms->APPn >= 0 && parms->APPn <= 15) { - cam->APPn = parms->APPn; - cam->APP_len = parms->APP_len; - memcpy(cam->APP_data, parms->APP_data, parms->APP_len); - } else { - LOG("Bad APPn Params n=%d len=%d\n", - parms->APPn, parms->APP_len); - return -EINVAL; - } - } else { - cam->APP_len = 0; - } - - if (parms->COM_len != 0) { - if (parms->COM_len > 0 && - parms->COM_len <= sizeof(cam->COM_data)) { - cam->COM_len = parms->COM_len; - memcpy(cam->COM_data, parms->COM_data, parms->COM_len); - } else { - LOG("Bad COM_len=%d\n", parms->COM_len); - return -EINVAL; - } - } - - return 0; -} - -/****************************************************************************** - * - * ioctl_reqbufs - * - * V4L2 Initiate memory mapping. - * NOTE: The user's request is ignored. For now the buffers are fixed. - * - *****************************************************************************/ - -static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) -{ - struct camera_data *cam = video_drvdata(file); - - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames); - req->count = cam->num_frames; - memset(&req->reserved, 0, sizeof(req->reserved)); - - return 0; -} - -/****************************************************************************** - * - * ioctl_querybuf - * - * V4L2 Query memory buffer status. - * - *****************************************************************************/ - -static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->index >= cam->num_frames) - return -EINVAL; - - buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; - buf->length = cam->frame_size; - - buf->memory = V4L2_MEMORY_MMAP; - - if (cam->mmapped) - buf->flags = V4L2_BUF_FLAG_MAPPED; - else - buf->flags = 0; - - buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - switch (cam->buffers[buf->index].status) { - case FRAME_EMPTY: - case FRAME_ERROR: - case FRAME_READING: - buf->bytesused = 0; - buf->flags = V4L2_BUF_FLAG_QUEUED; - break; - case FRAME_READY: - buf->bytesused = cam->buffers[buf->index].length; - v4l2_buffer_set_timestamp(buf, cam->buffers[buf->index].ts); - buf->sequence = cam->buffers[buf->index].seq; - buf->flags = V4L2_BUF_FLAG_DONE; - break; - } - - DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n", - buf->index, buf->m.offset, buf->flags, buf->sequence, - buf->bytesused); - - return 0; -} - -/****************************************************************************** - * - * ioctl_qbuf - * - * V4L2 User is freeing buffer - * - *****************************************************************************/ - -static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP || - buf->index >= cam->num_frames) - return -EINVAL; - - DBG("QBUF #%d\n", buf->index); - - if (cam->buffers[buf->index].status == FRAME_READY) - cam->buffers[buf->index].status = FRAME_EMPTY; - - return 0; -} - -/****************************************************************************** - * - * find_earliest_filled_buffer - * - * Helper for ioctl_dqbuf. Find the next ready buffer. - * - *****************************************************************************/ - -static int find_earliest_filled_buffer(struct camera_data *cam) -{ - int i; - int found = -1; - - for (i = 0; i < cam->num_frames; i++) { - if (cam->buffers[i].status == FRAME_READY) { - if (found < 0) { - found = i; - } else { - /* find which buffer is earlier */ - if (cam->buffers[i].ts < cam->buffers[found].ts) - found = i; - } - } - } - return found; -} - -/****************************************************************************** - * - * ioctl_dqbuf - * - * V4L2 User is asking for a filled buffer. - * - *****************************************************************************/ - -static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - struct camera_data *cam = video_drvdata(file); - int frame; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - frame = find_earliest_filled_buffer(cam); - - if (frame < 0 && file->f_flags & O_NONBLOCK) - return -EAGAIN; - - if (frame < 0) { - /* Wait for a frame to become available */ - struct framebuf *cb = cam->curbuff; - - mutex_unlock(&cam->v4l2_lock); - wait_event_interruptible(cam->wq_stream, - !video_is_registered(&cam->vdev) || - (cb = cam->curbuff)->status == FRAME_READY); - mutex_lock(&cam->v4l2_lock); - if (signal_pending(current)) - return -ERESTARTSYS; - if (!video_is_registered(&cam->vdev)) - return -ENOTTY; - frame = cb->num; - } - - buf->index = frame; - buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE - | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - buf->field = V4L2_FIELD_NONE; - v4l2_buffer_set_timestamp(buf, 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; - buf->reserved2 = 0; - buf->request_fd = 0; - memset(&buf->timecode, 0, sizeof(buf->timecode)); - - DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index, - cam->buffers[buf->index].status, buf->sequence, buf->bytesused); - - return 0; -} - -static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) -{ - struct camera_data *cam = video_drvdata(file); - int ret = -EINVAL; - - DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); - if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!cam->streaming) { - ret = cpia2_usb_stream_start(cam, - cam->params.camera_state.stream_mode); - if (!ret) - v4l2_ctrl_grab(cam->usb_alt, true); - } - return ret; -} - -static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) -{ - struct camera_data *cam = video_drvdata(file); - int ret = -EINVAL; - - DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); - if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->streaming) { - ret = cpia2_usb_stream_stop(cam); - if (!ret) - v4l2_ctrl_grab(cam->usb_alt, false); - } - return ret; -} - -/****************************************************************************** - * - * cpia2_mmap - * - *****************************************************************************/ -static int cpia2_mmap(struct file *file, struct vm_area_struct *area) -{ - struct camera_data *cam = video_drvdata(file); - int retval; - - if (mutex_lock_interruptible(&cam->v4l2_lock)) - return -ERESTARTSYS; - retval = cpia2_remap_buffer(cam, area); - - if (!retval) - cam->stream_fh = file->private_data; - mutex_unlock(&cam->v4l2_lock); - return retval; -} - -/****************************************************************************** - * - * reset_camera_struct_v4l - * - * Sets all values to the defaults - *****************************************************************************/ -static void reset_camera_struct_v4l(struct camera_data *cam) -{ - cam->width = cam->params.roi.width; - cam->height = cam->params.roi.height; - - cam->frame_size = buffer_size; - cam->num_frames = num_buffers; - - /* Flicker modes */ - cam->params.flicker_control.flicker_mode_req = flicker_mode; - - /* stream modes */ - cam->params.camera_state.stream_mode = alternate; - - cam->pixelformat = V4L2_PIX_FMT_JPEG; -} - -static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { - .vidioc_querycap = cpia2_querycap, - .vidioc_enum_input = cpia2_enum_input, - .vidioc_g_input = cpia2_g_input, - .vidioc_s_input = cpia2_s_input, - .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, - .vidioc_g_jpegcomp = cpia2_g_jpegcomp, - .vidioc_s_jpegcomp = cpia2_s_jpegcomp, - .vidioc_g_selection = cpia2_g_selection, - .vidioc_reqbufs = cpia2_reqbufs, - .vidioc_querybuf = cpia2_querybuf, - .vidioc_qbuf = cpia2_qbuf, - .vidioc_dqbuf = cpia2_dqbuf, - .vidioc_streamon = cpia2_streamon, - .vidioc_streamoff = cpia2_streamoff, - .vidioc_s_parm = cpia2_s_parm, - .vidioc_g_parm = cpia2_g_parm, - .vidioc_enum_framesizes = cpia2_enum_framesizes, - .vidioc_enum_frameintervals = cpia2_enum_frameintervals, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/*** - * The v4l video device structure initialized for this device - ***/ -static const struct v4l2_file_operations cpia2_fops = { - .owner = THIS_MODULE, - .open = cpia2_open, - .release = cpia2_close, - .read = cpia2_v4l_read, - .poll = cpia2_v4l_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = cpia2_mmap, -}; - -static const struct video_device cpia2_template = { - /* I could not find any place for the old .initialize initializer?? */ - .name = "CPiA2 Camera", - .fops = &cpia2_fops, - .ioctl_ops = &cpia2_ioctl_ops, - .release = video_device_release_empty, -}; - -void cpia2_camera_release(struct v4l2_device *v4l2_dev) -{ - struct camera_data *cam = - container_of(v4l2_dev, struct camera_data, v4l2_dev); - - v4l2_ctrl_handler_free(&cam->hdl); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); -} - -static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { - .s_ctrl = cpia2_s_ctrl, -}; - -/****************************************************************************** - * - * cpia2_register_camera - * - *****************************************************************************/ -int cpia2_register_camera(struct camera_data *cam) -{ - struct v4l2_ctrl_handler *hdl = &cam->hdl; - struct v4l2_ctrl_config cpia2_usb_alt = { - .ops = &cpia2_ctrl_ops, - .id = CPIA2_CID_USB_ALT, - .name = "USB Alternate", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = USBIF_ISO_1, - .max = USBIF_ISO_6, - .step = 1, - }; - int ret; - - v4l2_ctrl_handler_init(hdl, 12); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_BRIGHTNESS, - cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, - 255, 1, DEFAULT_BRIGHTNESS); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_JPEG_ACTIVE_MARKER, 0, - V4L2_JPEG_ACTIVE_MARKER_DHT, 0, - V4L2_JPEG_ACTIVE_MARKER_DHT); - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, - 100, 1, 100); - cpia2_usb_alt.def = alternate; - cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); - /* VP5 Only */ - if (cam->params.pnp_id.device_type != DEVICE_STV_672) - v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - /* Flicker control only valid for 672 */ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, - V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - 0, 0); - /* Light control only valid for the QX5 Microscope */ - if (cam->params.pnp_id.product == 0x151) { - cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_ILLUMINATORS_1, - 0, 1, 1, 0); - cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, - V4L2_CID_ILLUMINATORS_2, - 0, 1, 1, 0); - v4l2_ctrl_cluster(2, &cam->top_light); - } - - if (hdl->error) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - cam->vdev = cpia2_template; - video_set_drvdata(&cam->vdev, cam); - cam->vdev.lock = &cam->v4l2_lock; - cam->vdev.ctrl_handler = hdl; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - reset_camera_struct_v4l(cam); - - /* register v4l device */ - if (video_register_device(&cam->vdev, VFL_TYPE_VIDEO, video_nr) < 0) { - ERR("video_register_device failed\n"); - return -ENODEV; - } - - return 0; -} - -/****************************************************************************** - * - * cpia2_unregister_camera - * - *****************************************************************************/ -void cpia2_unregister_camera(struct camera_data *cam) -{ - video_unregister_device(&cam->vdev); -} - -/****************************************************************************** - * - * check_parameters - * - * Make sure that all user-supplied parameters are sensible - *****************************************************************************/ -static void __init check_parameters(void) -{ - if (buffer_size < PAGE_SIZE) { - buffer_size = PAGE_SIZE; - LOG("buffer_size too small, setting to %d\n", buffer_size); - } else if (buffer_size > 1024 * 1024) { - /* arbitrary upper limiit */ - buffer_size = 1024 * 1024; - LOG("buffer_size ridiculously large, setting to %d\n", - buffer_size); - } else { - buffer_size += PAGE_SIZE - 1; - buffer_size &= ~(PAGE_SIZE - 1); - } - - if (num_buffers < 1) { - num_buffers = 1; - LOG("num_buffers too small, setting to %d\n", num_buffers); - } else if (num_buffers > VIDEO_MAX_FRAME) { - num_buffers = VIDEO_MAX_FRAME; - LOG("num_buffers too large, setting to %d\n", num_buffers); - } - - if (alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) { - alternate = DEFAULT_ALT; - LOG("alternate specified is invalid, using %d\n", alternate); - } - - if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { - flicker_mode = 0; - LOG("Flicker mode specified is invalid, using %d\n", - flicker_mode); - } - - DBG("Using %d buffers, each %d bytes, alternate=%d\n", - num_buffers, buffer_size, alternate); -} - -/************ Module Stuff ***************/ - -/****************************************************************************** - * - * cpia2_init/module_init - * - *****************************************************************************/ -static int __init cpia2_init(void) -{ - LOG("%s v%s\n", - ABOUT, CPIA_VERSION); - check_parameters(); - return cpia2_usb_init(); -} - -/****************************************************************************** - * - * cpia2_exit/module_exit - * - *****************************************************************************/ -static void __exit cpia2_exit(void) -{ - cpia2_usb_cleanup(); - schedule_timeout(2 * HZ); -} - -module_init(cpia2_init); -module_exit(cpia2_exit); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index cc21b983f954..9781080c6e7d 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -55,6 +55,7 @@ menuconfig STAGING_MEDIA_DEPRECATED If in doubt, say N here. if STAGING_MEDIA_DEPRECATED +source "drivers/staging/media/deprecated/cpia2/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 804875a479be..adcf128d27b4 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ +obj-$(CONFIG_VIDEO_CPIA2) += deprecated/cpia2/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ diff --git a/drivers/staging/media/deprecated/cpia2/Kconfig b/drivers/staging/media/deprecated/cpia2/Kconfig new file mode 100644 index 000000000000..ee3b25a759d4 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_CPIA2 + tristate "CPiA2 Video For Linux (DEPRECATED)" + depends on USB && VIDEO_DEV + help + This is the video4linux driver for cameras based on Vision's CPiA2 + (Colour Processor Interface ASIC), such as the Digital Blue QX5 + Microscope. If you have one of these cameras, say Y here + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + This driver is also available as a module (cpia2). diff --git a/drivers/staging/media/deprecated/cpia2/Makefile b/drivers/staging/media/deprecated/cpia2/Makefile new file mode 100644 index 000000000000..05664141f4d7 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o + +obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o diff --git a/drivers/staging/media/deprecated/cpia2/TODO b/drivers/staging/media/deprecated/cpia2/TODO new file mode 100644 index 000000000000..92ac8718d164 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/TODO @@ -0,0 +1,6 @@ +The cpia2 driver does not use the vb2 framework for streaming +video, instead it implements this in the driver. + +To prevent removal of this driver early 2023 it has to be +converted to use vb2. Contact the linux-media@vger.kernel.org +mailing list if you want to do this. diff --git a/drivers/staging/media/deprecated/cpia2/cpia2.h b/drivers/staging/media/deprecated/cpia2/cpia2.h new file mode 100644 index 000000000000..57b7f1ea68da --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2.h @@ -0,0 +1,475 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/**************************************************************************** + * + * Filename: cpia2.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPiA2 based video cameras. + * + * This driver is modelled on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + ****************************************************************************/ + +#ifndef __CPIA2_H__ +#define __CPIA2_H__ + +#include +#include +#include +#include +#include +#include + +#include "cpia2_registers.h" + +/* define for verbose debug output */ +//#define _CPIA2_DEBUG_ + +/*** + * Image defines + ***/ + +/* Misc constants */ +#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */ + +/* USB Transfer mode */ +#define XFER_ISOC 0 +#define XFER_BULK 1 + +/* USB Alternates */ +#define USBIF_CMDONLY 0 +#define USBIF_BULK 1 +#define USBIF_ISO_1 2 /* 128 bytes/ms */ +#define USBIF_ISO_2 3 /* 384 bytes/ms */ +#define USBIF_ISO_3 4 /* 640 bytes/ms */ +#define USBIF_ISO_4 5 /* 768 bytes/ms */ +#define USBIF_ISO_5 6 /* 896 bytes/ms */ +#define USBIF_ISO_6 7 /* 1023 bytes/ms */ + +/* Flicker Modes */ +#define NEVER_FLICKER 0 +#define FLICKER_60 60 +#define FLICKER_50 50 + +/* Debug flags */ +#define DEBUG_NONE 0 +#define DEBUG_REG 0x00000001 +#define DEBUG_DUMP_PATCH 0x00000002 +#define DEBUG_DUMP_REGS 0x00000004 + +/*** + * Video frame sizes + ***/ +enum { + VIDEOSIZE_VGA = 0, /* 640x480 */ + VIDEOSIZE_CIF, /* 352x288 */ + VIDEOSIZE_QVGA, /* 320x240 */ + VIDEOSIZE_QCIF, /* 176x144 */ + VIDEOSIZE_288_216, + VIDEOSIZE_256_192, + VIDEOSIZE_224_168, + VIDEOSIZE_192_144, +}; + +#define STV_IMAGE_CIF_ROWS 288 +#define STV_IMAGE_CIF_COLS 352 + +#define STV_IMAGE_QCIF_ROWS 144 +#define STV_IMAGE_QCIF_COLS 176 + +#define STV_IMAGE_VGA_ROWS 480 +#define STV_IMAGE_VGA_COLS 640 + +#define STV_IMAGE_QVGA_ROWS 240 +#define STV_IMAGE_QVGA_COLS 320 + +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ + +/*** + * Enums + ***/ +/* Sensor types available with cpia2 asics */ +enum sensors { + CPIA2_SENSOR_410, + CPIA2_SENSOR_500 +}; + +/* Asic types available in the CPiA2 architecture */ +#define CPIA2_ASIC_672 0x67 + +/* Device types (stv672, stv676, etc) */ +#define DEVICE_STV_672 0x0001 +#define DEVICE_STV_676 0x0002 + +enum frame_status { + FRAME_EMPTY, + FRAME_READING, /* In the process of being grabbed into */ + FRAME_READY, /* Ready to be read */ + FRAME_ERROR, +}; + +/*** + * Register access (for USB request byte) + ***/ +enum { + CAMERAACCESS_SYSTEM = 0, + CAMERAACCESS_VC, + CAMERAACCESS_VP, + CAMERAACCESS_IDATA +}; + +#define CAMERAACCESS_TYPE_BLOCK 0x00 +#define CAMERAACCESS_TYPE_RANDOM 0x04 +#define CAMERAACCESS_TYPE_MASK 0x08 +#define CAMERAACCESS_TYPE_REPEAT 0x0C + +#define TRANSFER_READ 0 +#define TRANSFER_WRITE 1 + +#define DEFAULT_ALT USBIF_ISO_6 +#define DEFAULT_BRIGHTNESS 0x46 +#define DEFAULT_CONTRAST 0x93 +#define DEFAULT_SATURATION 0x7f + +/* Power state */ +#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER +#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER + + +/******** + * Commands + *******/ +enum { + CPIA2_CMD_NONE = 0, + CPIA2_CMD_GET_VERSION, + CPIA2_CMD_GET_PNP_ID, + CPIA2_CMD_GET_ASIC_TYPE, + CPIA2_CMD_GET_SENSOR, + CPIA2_CMD_GET_VP_DEVICE, + CPIA2_CMD_GET_VP_BRIGHTNESS, + CPIA2_CMD_SET_VP_BRIGHTNESS, + CPIA2_CMD_GET_CONTRAST, + CPIA2_CMD_SET_CONTRAST, + CPIA2_CMD_GET_VP_SATURATION, + CPIA2_CMD_SET_VP_SATURATION, + CPIA2_CMD_GET_VP_GPIO_DIRECTION, + CPIA2_CMD_SET_VP_GPIO_DIRECTION, + CPIA2_CMD_GET_VP_GPIO_DATA, + CPIA2_CMD_SET_VP_GPIO_DATA, + CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_GET_VC_MP_GPIO_DATA, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_CMD_ENABLE_PACKET_CTRL, + CPIA2_CMD_GET_FLICKER_MODES, + CPIA2_CMD_SET_FLICKER_MODES, + CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */ + CPIA2_CMD_SET_HI_POWER, + CPIA2_CMD_SET_LOW_POWER, + CPIA2_CMD_CLEAR_V2W_ERR, + CPIA2_CMD_SET_USER_MODE, + CPIA2_CMD_GET_USER_MODE, + CPIA2_CMD_FRAMERATE_REQ, + CPIA2_CMD_SET_COMPRESSION_STATE, + CPIA2_CMD_GET_WAKEUP, + CPIA2_CMD_SET_WAKEUP, + CPIA2_CMD_GET_PW_CONTROL, + CPIA2_CMD_SET_PW_CONTROL, + CPIA2_CMD_GET_SYSTEM_CTRL, + CPIA2_CMD_SET_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_SYSTEM_STATE, + CPIA2_CMD_GET_VP_SYSTEM_CTRL, + CPIA2_CMD_SET_VP_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_EXP_MODES, + CPIA2_CMD_SET_VP_EXP_MODES, + CPIA2_CMD_GET_DEVICE_CONFIG, + CPIA2_CMD_SET_DEVICE_CONFIG, + CPIA2_CMD_SET_SERIAL_ADDR, + CPIA2_CMD_SET_SENSOR_CR1, + CPIA2_CMD_GET_VC_CONTROL, + CPIA2_CMD_SET_VC_CONTROL, + CPIA2_CMD_SET_TARGET_KB, + CPIA2_CMD_SET_DEF_JPEG_OPT, + CPIA2_CMD_REHASH_VP4, + CPIA2_CMD_GET_USER_EFFECTS, + CPIA2_CMD_SET_USER_EFFECTS +}; + +enum user_cmd { + COMMAND_NONE = 0x00000001, + COMMAND_SET_FPS = 0x00000002, + COMMAND_SET_COLOR_PARAMS = 0x00000004, + COMMAND_GET_COLOR_PARAMS = 0x00000008, + COMMAND_SET_FORMAT = 0x00000010, /* size, etc */ + COMMAND_SET_FLICKER = 0x00000020 +}; + +/*** + * Some defines specific to the 676 chip + ***/ +#define CAMACC_CIF 0x01 +#define CAMACC_VGA 0x02 +#define CAMACC_QCIF 0x04 +#define CAMACC_QVGA 0x08 + + +struct cpia2_register { + u8 index; + u8 value; +}; + +struct cpia2_reg_mask { + u8 index; + u8 and_mask; + u8 or_mask; + u8 fill; +}; + +struct cpia2_command { + u32 command; + u8 req_mode; /* (Block or random) | registerBank */ + u8 reg_count; + u8 direction; + u8 start; + union reg_types { + struct cpia2_register registers[32]; + struct cpia2_reg_mask masks[16]; + u8 block_data[64]; + u8 *patch_data; /* points to function defined block */ + } buffer; +}; + +struct camera_params { + struct { + u8 firmware_revision_hi; /* For system register set (bank 0) */ + u8 firmware_revision_lo; + u8 asic_id; /* Video Compressor set (bank 1) */ + u8 asic_rev; + u8 vp_device_hi; /* Video Processor set (bank 2) */ + u8 vp_device_lo; + u8 sensor_flags; + u8 sensor_rev; + } version; + + struct { + u32 device_type; /* enumerated from vendor/product ids. + * Currently, either STV_672 or STV_676 */ + u16 vendor; + u16 product; + u16 device_revision; + } pnp_id; + + struct { + u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */ + u8 contrast; /* Note: this is CPIA2_VP_YRANGE */ + u8 saturation; /* CPIA2_VP_SATURATION */ + } color_params; + + struct { + u8 cam_register; + u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ + } flicker_control; + + struct { + u8 jpeg_options; + u8 creep_period; + u8 user_squeeze; + u8 inhibit_htables; + } compression; + + struct { + u8 ohsize; /* output image size */ + u8 ovsize; + u8 hcrop; /* cropping start_pos/4 */ + u8 vcrop; + u8 hphase; /* scaling registers */ + u8 vphase; + u8 hispan; + u8 vispan; + u8 hicrop; + u8 vicrop; + u8 hifraction; + u8 vifraction; + } image_size; + + struct { + int width; /* actual window width */ + int height; /* actual window height */ + } roi; + + struct { + u8 video_mode; + u8 frame_rate; + u8 video_size; /* Not a register, just a convenience for cropped sizes */ + u8 gpio_direction; + u8 gpio_data; + u8 system_ctrl; + u8 system_state; + u8 lowlight_boost; /* Bool: 0 = off, 1 = on */ + u8 device_config; + u8 exposure_modes; + u8 user_effects; + } vp_params; + + struct { + u8 pw_control; + u8 wakeup; + u8 vc_control; + u8 vc_mp_direction; + u8 vc_mp_data; + u8 quality; + } vc_params; + + struct { + u8 power_mode; + u8 system_ctrl; + u8 stream_mode; /* This is the current alternate for usb drivers */ + u8 allow_corrupt; + } camera_state; +}; + +#define NUM_SBUF 2 + +struct cpia2_sbuf { + char *data; + struct urb *urb; +}; + +struct framebuf { + u64 ts; + unsigned long seq; + int num; + int length; + int max_length; + volatile enum frame_status status; + u8 *data; + struct framebuf *next; +}; + +struct camera_data { + /* locks */ + struct v4l2_device v4l2_dev; + struct mutex v4l2_lock; /* serialize file operations */ + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; + + /* camera status */ + int first_image_seen; + enum sensors sensor_type; + u8 flush; + struct v4l2_fh *stream_fh; + u8 mmapped; + int streaming; /* 0 = no, 1 = yes */ + int xfer_mode; /* XFER_BULK or XFER_ISOC */ + struct camera_params params; /* camera settings */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + struct video_device vdev; /* v4l videodev */ + u32 width; + u32 height; /* Its size */ + __u32 pixelformat; /* Format fourcc */ + + /* USB */ + struct usb_device *dev; + unsigned char iface; + unsigned int cur_alt; + unsigned int old_alt; + struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */ + + wait_queue_head_t wq_stream; + + /* Buffering */ + u32 frame_size; + int num_frames; + unsigned long frame_count; + u8 *frame_buffer; /* frame buffer data */ + struct framebuf *buffers; + struct framebuf * volatile curbuff; + struct framebuf *workbuff; + + /* MJPEG Extension */ + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ +}; + +/* v4l */ +int cpia2_register_camera(struct camera_data *cam); +void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); + +/* core */ +int cpia2_reset_camera(struct camera_data *cam); +int cpia2_set_low_power(struct camera_data *cam); +void cpia2_dbg_dump_registers(struct camera_data *cam); +int cpia2_match_video_size(int width, int height); +void cpia2_set_camera_state(struct camera_data *cam); +void cpia2_save_camera_state(struct camera_data *cam); +void cpia2_set_color_params(struct camera_data *cam); +void cpia2_set_brightness(struct camera_data *cam, unsigned char value); +void cpia2_set_contrast(struct camera_data *cam, unsigned char value); +void cpia2_set_saturation(struct camera_data *cam, unsigned char value); +int cpia2_set_flicker_mode(struct camera_data *cam, int mode); +void cpia2_set_format(struct camera_data *cam); +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); +int cpia2_do_command(struct camera_data *cam, + unsigned int command, + unsigned char direction, unsigned char param); +void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); +int cpia2_init_camera(struct camera_data *cam); +int cpia2_allocate_buffers(struct camera_data *cam); +void cpia2_free_buffers(struct camera_data *cam); +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock); +__poll_t cpia2_poll(struct camera_data *cam, + struct file *filp, poll_table *wait); +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); +void cpia2_set_property_flip(struct camera_data *cam, int prop_val); +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); +int cpia2_set_fps(struct camera_data *cam, int framerate); + +/* usb */ +int cpia2_usb_init(void); +void cpia2_usb_cleanup(void); +int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers, + u8 request, u8 start, u8 count, u8 direction); +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate); +int cpia2_usb_stream_stop(struct camera_data *cam); +int cpia2_usb_stream_pause(struct camera_data *cam); +int cpia2_usb_stream_resume(struct camera_data *cam); +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt); + + +/* ----------------------- debug functions ---------------------- */ +#ifdef _CPIA2_DEBUG_ +#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args) +#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args) +#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args) +#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args) +#else +#define ALOG(fmt,args...) printk(fmt,##args) +#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args) +#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args) +#define DBG(fmn,args...) do {} while(0) +#endif +/* No function or lineno, for shorter lines */ +#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args) + +#endif diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_core.c b/drivers/staging/media/deprecated/cpia2/cpia2_core.c new file mode 100644 index 000000000000..b5a2d06fb356 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_core.c @@ -0,0 +1,2434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_core.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + * + ****************************************************************************/ + +#include "cpia2.h" + +#include +#include +#include +#include +#include +#include + +#define FIRMWARE "cpia2/stv0672_vp4.bin" +MODULE_FIRMWARE(FIRMWARE); + +/* #define _CPIA2_DEBUG_ */ + +#ifdef _CPIA2_DEBUG_ + +static const char *block_name[] = { + "System", + "VC", + "VP", + "IDATA" +}; +#endif + +static unsigned int debugs_on; /* default 0 - DEBUG_REG */ + + +/****************************************************************************** + * + * Forward Declarations + * + *****************************************************************************/ +static int apply_vp_patch(struct camera_data *cam); +static int set_default_user_mode(struct camera_data *cam); +static int set_vw_size(struct camera_data *cam, int size); +static int configure_sensor(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_410(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_500(struct camera_data *cam, + int reqwidth, int reqheight); +static int set_all_properties(struct camera_data *cam); +static void wake_system(struct camera_data *cam); +static void set_lowlight_boost(struct camera_data *cam); +static void reset_camera_struct(struct camera_data *cam); +static int cpia2_set_high_power(struct camera_data *cam); + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + /* Round it off to PAGE_SIZE */ + size = PAGE_ALIGN(size); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + + while ((long)size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + size = PAGE_ALIGN(size); + + adr = (unsigned long) mem; + while ((long)size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/****************************************************************************** + * + * cpia2_do_command + * + * Send an arbitrary command to the camera. For commands that read from + * the camera, copy the buffers into the proper param structures. + *****************************************************************************/ +int cpia2_do_command(struct camera_data *cam, + u32 command, u8 direction, u8 param) +{ + int retval = 0; + struct cpia2_command cmd; + unsigned int device = cam->params.pnp_id.device_type; + + cmd.command = command; + cmd.reg_count = 2; /* default */ + cmd.direction = direction; + + /*** + * Set up the command. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_DEVICE_HI; + break; + case CPIA2_CMD_GET_PNP_ID: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 8; + cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI; + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_ASIC_ID; + break; + case CPIA2_CMD_GET_SENSOR: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_SENSOR_FLAGS; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_DEVICEH; + break; + case CPIA2_CMD_SET_VP_BRIGHTNESS: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_BRIGHTNESS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_EXPOSURE_TARGET; + else + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + break; + case CPIA2_CMD_SET_CONTRAST: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_CONTRAST: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_YRANGE; + break; + case CPIA2_CMD_SET_VP_SATURATION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_SATURATION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP_SATURATION; + else + cmd.start = CPIA2_VP5_MCUVSATURATION; + break; + case CPIA2_CMD_SET_VP_GPIO_DATA: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DATA; + break; + case CPIA2_CMD_SET_VP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DIRECTION; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DATA: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DATA; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DIR; + break; + case CPIA2_CMD_ENABLE_PACKET_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.reg_count = 1; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_FLICKER_MODES: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_FLICKER_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_FLICKER_MODES; + break; + case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 2; + cmd.start = 0; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | + CPIA2_VC_ST_CTRL_FIFO_ENABLE; + break; + case CPIA2_CMD_SET_HI_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[1].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + cmd.buffer.registers[1].value = + CPIA2_SYSTEM_CONTROL_HIGH_POWER; + break; + case CPIA2_CMD_SET_LOW_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = 0; + break; + case CPIA2_CMD_CLEAR_V2W_ERR: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + break; + case CPIA2_CMD_SET_USER_MODE: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_USER_MODE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_MODE; + else + cmd.start = CPIA2_VP5_USER_MODE; + break; + case CPIA2_CMD_FRAMERATE_REQ: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_FRAMERATE_REQUEST; + else + cmd.start = CPIA2_VP5_FRAMERATE_REQUEST; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_WAKEUP: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_WAKEUP: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_WAKEUP; + break; + case CPIA2_CMD_SET_PW_CONTROL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_PW_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_PW_CTRL; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMSTATE; + break; + case CPIA2_CMD_SET_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_SYSTEM_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + break; + case CPIA2_CMD_SET_VP_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMCTRL; + break; + case CPIA2_CMD_SET_VP_EXP_MODES: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_EXP_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_EXPOSURE_MODES; + break; + case CPIA2_CMD_SET_DEVICE_CONFIG: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_DEVICE_CONFIG: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_DEVICE_CONFIG; + break; + case CPIA2_CMD_SET_SERIAL_ADDR: + cmd.buffer.block_data[0] = param; + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR; + break; + case CPIA2_CMD_SET_SENSOR_CR1: + cmd.buffer.block_data[0] = param; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_SENSOR_CR1; + break; + case CPIA2_CMD_SET_VC_CONTROL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_VC_CTRL; + break; + case CPIA2_CMD_SET_TARGET_KB: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB; + cmd.buffer.registers[0].value = param; + break; + case CPIA2_CMD_SET_DEF_JPEG_OPT: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[0].value = + CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE; + cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE; + cmd.buffer.registers[1].value = 20; + cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD; + cmd.buffer.registers[2].value = 2; + cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + break; + case CPIA2_CMD_REHASH_VP4: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_REHASH_VALUES; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as + this register can also affect + flicker modes */ + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_USER_EFFECTS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_EFFECTS; + else + cmd.start = CPIA2_VP5_USER_EFFECTS; + break; + default: + LOG("DoCommand received invalid command\n"); + return -EINVAL; + } + + retval = cpia2_send_command(cam, &cmd); + if (retval) { + return retval; + } + + /*** + * Now copy any results from a read into the appropriate param struct. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cam->params.version.firmware_revision_hi = + cmd.buffer.block_data[0]; + cam->params.version.firmware_revision_lo = + cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_PNP_ID: + cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) | + cmd.buffer.block_data[1]; + cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) | + cmd.buffer.block_data[3]; + cam->params.pnp_id.device_revision = + (cmd.buffer.block_data[4] << 8) | + cmd.buffer.block_data[5]; + if (cam->params.pnp_id.vendor == 0x553) { + if (cam->params.pnp_id.product == 0x100) { + cam->params.pnp_id.device_type = DEVICE_STV_672; + } else if (cam->params.pnp_id.product == 0x140 || + cam->params.pnp_id.product == 0x151) { + cam->params.pnp_id.device_type = DEVICE_STV_676; + } + } + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cam->params.version.asic_id = cmd.buffer.block_data[0]; + cam->params.version.asic_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_SENSOR: + cam->params.version.sensor_flags = cmd.buffer.block_data[0]; + cam->params.version.sensor_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; + cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_GPIO_DATA: + cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_FLICKER_MODES: + cam->params.flicker_control.cam_register = + cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_WAKEUP: + cam->params.vc_params.wakeup = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_PW_CONTROL: + cam->params.vc_params.pw_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_SYSTEM_CTRL: + cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cam->params.vp_params.system_state = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_EXP_MODES: + cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_DEVICE_CONFIG: + cam->params.vp_params.device_config = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_CONTROL: + cam->params.vc_params.vc_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_MODE: + cam->params.vp_params.video_mode = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_EFFECTS: + cam->params.vp_params.user_effects = cmd.buffer.block_data[0]; + break; + default: + break; + } + return retval; +} + +/****************************************************************************** + * + * cpia2_send_command + * + *****************************************************************************/ + +#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") +#define BINDEX(cmd) (cmd->req_mode & 0x03) + +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) +{ + u8 count; + u8 start; + u8 *buffer; + int retval; + + switch (cmd->req_mode & 0x0c) { + case CAMERAACCESS_TYPE_RANDOM: + count = cmd->reg_count * sizeof(struct cpia2_register); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Random: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_BLOCK: + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Block: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_MASK: + count = cmd->reg_count * sizeof(struct cpia2_reg_mask); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Mask: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Repeat: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + default: + LOG("%s: invalid request mode\n",__func__); + return -EINVAL; + } + + retval = cpia2_usb_transfer_cmd(cam, + buffer, + cmd->req_mode, + start, count, cmd->direction); +#ifdef _CPIA2_DEBUG_ + if (debugs_on & DEBUG_REG) { + int i; + for (i = 0; i < cmd->reg_count; i++) { + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) + KINFO("%s Block: [0x%02X] = 0x%02X\n", + DIR(cmd), start + i, buffer[i]); + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) + KINFO("%s Random: [0x%02X] = 0x%02X\n", + DIR(cmd), cmd->buffer.registers[i].index, + cmd->buffer.registers[i].value); + } + } +#endif + + return retval; +}; + +/************* + * Functions to implement camera functionality + *************/ +/****************************************************************************** + * + * cpia2_get_version_info + * + *****************************************************************************/ +static void cpia2_get_version_info(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0); +} + +/****************************************************************************** + * + * cpia2_reset_camera + * + * Called at least during the open process, sets up initial params. + *****************************************************************************/ +int cpia2_reset_camera(struct camera_data *cam) +{ + u8 tmp_reg; + int retval = 0; + int target_kb; + int i; + struct cpia2_command cmd; + + /*** + * VC setup + ***/ + retval = configure_sensor(cam, + cam->params.roi.width, + cam->params.roi.height); + if (retval < 0) { + ERR("Couldn't configure sensor, error=%d\n", retval); + return retval; + } + + /* Clear FIFO and route/enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE; + + cpia2_send_command(cam, &cmd); + + cpia2_set_high_power(cam); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + /* Enable button notification */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.buffer.registers[0].value = + CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + } + + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + retval = apply_vp_patch(cam); + + /* wait for vp to go to sleep */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + /*** + * If this is a 676, apply VP5 fixes before we start streaming + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) { + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + + /* The following writes improve the picture */ + cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL; + cmd.buffer.registers[0].value = 0; /* reduce from the default + * rec 601 pedestal of 16 */ + cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE; + cmd.buffer.registers[1].value = 0x92; /* increase from 100% to + * (256/256 - 31) to fill + * available range */ + cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING; + cmd.buffer.registers[2].value = 0xFF; /* Increase from the + * default rec 601 ceiling + * of 240 */ + cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION; + cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec + * 601 100% level (128) + * to 145-192 */ + cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP; + cmd.buffer.registers[4].value = 0x80; /* Inhibit the + * anti-flicker */ + + /* The following 4 writes are a fix to allow QVGA to work at 30 fps */ + cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H; + cmd.buffer.registers[5].value = 0x01; + cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L; + cmd.buffer.registers[6].value = 0xE3; + cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[7].value = 0x02; + cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[8].value = 0xFC; + + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 9; + + cpia2_send_command(cam, &cmd); + } + + /* Activate all settings and start the data stream */ + /* Set user mode */ + set_default_user_mode(cam); + + /* Give VP time to wake up */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + set_all_properties(cam); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After SetAllProperties(cam), user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + /*** + * Set audio regulator off. This and the code to set the compresison + * state are too complex to form a CPIA2_CMD_, and seem to be somewhat + * intertwined. This stuff came straight from the windows driver. + ***/ + /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + tmp_reg = cam->params.vp_params.system_ctrl; + cmd.buffer.registers[0].value = tmp_reg & + (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF)); + + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = cam->params.vp_params.device_config | + CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE; + cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL; + cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + /* Set the correct I2C address in the CPiA-2 system register */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR); + + /* Now have sensor access - set bit to turn the audio regulator off */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SENSOR_CR1, + TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR); + + /* Set the correct I2C address in the CPiA-2 system register */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88 + else + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a + + /* increase signal drive strength */ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) + cpia2_do_command(cam, + CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, + CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP); + + /* Start autoexposure */ + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[0].value = cam->params.vp_params.device_config & + (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF); + + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = + cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL; + + cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG; + cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + + cpia2_send_command(cam, &cmd); + + /* Set compression state */ + cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0); + if (cam->params.compression.inhibit_htables) { + tmp_reg = cam->params.vc_params.vc_control | + CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } else { + tmp_reg = cam->params.vc_params.vc_control & + ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } + cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); + + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; + cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, + TRANSFER_WRITE, target_kb); + + /* Wiggle VC Reset */ + /*** + * First read and wait a bit. + ***/ + for (i = 0; i < 50; i++) { + cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL, + TRANSFER_READ, 0); + } + + tmp_reg = cam->params.vc_params.pw_control; + tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N; + + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N; + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After VC RESET, user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_high_power + * + *****************************************************************************/ +static int cpia2_set_high_power(struct camera_data *cam) +{ + int i; + for (i = 0; i <= 50; i++) { + /* Read system status */ + cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0); + + /* If there is an error, clear it */ + if(cam->params.camera_state.system_ctrl & + CPIA2_SYSTEM_CONTROL_V2W_ERR) + cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR, + TRANSFER_WRITE, 0); + + /* Try to set high power mode */ + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, + TRANSFER_WRITE, 1); + + /* Try to read something in VP to check if everything is awake */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE, + TRANSFER_READ, 0); + if (cam->params.vp_params.system_state & + CPIA2_VP_SYSTEMSTATE_HK_ALIVE) { + break; + } else if (i == 50) { + cam->params.camera_state.power_mode = LO_POWER_MODE; + ERR("Camera did not wake up\n"); + return -EIO; + } + } + + DBG("System now in high power state\n"); + cam->params.camera_state.power_mode = HI_POWER_MODE; + return 0; +} + +/****************************************************************************** + * + * cpia2_set_low_power + * + *****************************************************************************/ +int cpia2_set_low_power(struct camera_data *cam) +{ + cam->params.camera_state.power_mode = LO_POWER_MODE; + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0); + return 0; +} + +/****************************************************************************** + * + * apply_vp_patch + * + *****************************************************************************/ +static int cpia2_send_onebyte_command(struct camera_data *cam, + struct cpia2_command *cmd, + u8 start, u8 datum) +{ + cmd->buffer.block_data[0] = datum; + cmd->start = start; + cmd->reg_count = 1; + return cpia2_send_command(cam, cmd); +} + +static int apply_vp_patch(struct camera_data *cam) +{ + const struct firmware *fw; + const char fw_name[] = FIRMWARE; + int i, ret; + struct cpia2_command cmd; + + ret = request_firmware(&fw, fw_name, &cam->dev->dev); + if (ret) { + printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n", + fw_name); + return ret; + } + + cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP; + cmd.direction = TRANSFER_WRITE; + + /* First send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the data payload */ + for (i = 2; i < fw->size; i += 64) { + cmd.start = 0x0C; /* Data */ + cmd.reg_count = min_t(uint, 64, fw->size - i); + memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count); + cpia2_send_command(cam, &cmd); + } + + /* Next send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the 'goto' command */ + cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1); + + release_firmware(fw); + return 0; +} + +/****************************************************************************** + * + * set_default_user_mode + * + *****************************************************************************/ +static int set_default_user_mode(struct camera_data *cam) +{ + unsigned char user_mode; + unsigned char frame_rate; + int width = cam->params.roi.width; + int height = cam->params.roi.height; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + if ((width > STV_IMAGE_QCIF_COLS) + || (height > STV_IMAGE_QCIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_CIF; + } else { + user_mode = CPIA2_VP_USER_MODE_QCIFDS; + } + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + case CPIA2_VP_SENSOR_FLAGS_500: + if ((width > STV_IMAGE_CIF_COLS) + || (height > STV_IMAGE_CIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_VGA; + } else { + user_mode = CPIA2_VP_USER_MODE_QVGADS; + } + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + frame_rate = CPIA2_VP_FRAMERATE_15; + else + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + default: + LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, + cam->params.version.sensor_flags); + return -EINVAL; + } + + DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n", + cam->params.version.sensor_flags, user_mode, frame_rate); + cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE, + user_mode); + if(cam->params.vp_params.frame_rate > 0 && + frame_rate > cam->params.vp_params.frame_rate) + frame_rate = cam->params.vp_params.frame_rate; + + cpia2_set_fps(cam, frame_rate); + +// if (cam->params.pnp_id.device_type == DEVICE_STV_676) +// cpia2_do_command(cam, +// CPIA2_CMD_SET_VP_SYSTEM_CTRL, +// TRANSFER_WRITE, +// CPIA2_VP_SYSTEMCTRL_HK_CONTROL | +// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL); + + return 0; +} + +/****************************************************************************** + * + * cpia2_match_video_size + * + * return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. + *****************************************************************************/ +int cpia2_match_video_size(int width, int height) +{ + if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS) + return VIDEOSIZE_VGA; + + if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS) + return VIDEOSIZE_CIF; + + if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS) + return VIDEOSIZE_QVGA; + + if (width >= 288 && height >= 216) + return VIDEOSIZE_288_216; + + if (width >= 256 && height >= 192) + return VIDEOSIZE_256_192; + + if (width >= 224 && height >= 168) + return VIDEOSIZE_224_168; + + if (width >= 192 && height >= 144) + return VIDEOSIZE_192_144; + + if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS) + return VIDEOSIZE_QCIF; + + return -1; +} + +/****************************************************************************** + * + * SetVideoSize + * + *****************************************************************************/ +static int set_vw_size(struct camera_data *cam, int size) +{ + int retval = 0; + + cam->params.vp_params.video_size = size; + + switch (size) { + case VIDEOSIZE_VGA: + DBG("Setting size to VGA\n"); + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + cam->width = STV_IMAGE_VGA_COLS; + cam->height = STV_IMAGE_VGA_ROWS; + break; + case VIDEOSIZE_CIF: + DBG("Setting size to CIF\n"); + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + cam->width = STV_IMAGE_CIF_COLS; + cam->height = STV_IMAGE_CIF_ROWS; + break; + case VIDEOSIZE_QVGA: + DBG("Setting size to QVGA\n"); + cam->params.roi.width = STV_IMAGE_QVGA_COLS; + cam->params.roi.height = STV_IMAGE_QVGA_ROWS; + cam->width = STV_IMAGE_QVGA_COLS; + cam->height = STV_IMAGE_QVGA_ROWS; + break; + case VIDEOSIZE_288_216: + cam->params.roi.width = 288; + cam->params.roi.height = 216; + cam->width = 288; + cam->height = 216; + break; + case VIDEOSIZE_256_192: + cam->width = 256; + cam->height = 192; + cam->params.roi.width = 256; + cam->params.roi.height = 192; + break; + case VIDEOSIZE_224_168: + cam->width = 224; + cam->height = 168; + cam->params.roi.width = 224; + cam->params.roi.height = 168; + break; + case VIDEOSIZE_192_144: + cam->width = 192; + cam->height = 144; + cam->params.roi.width = 192; + cam->params.roi.height = 144; + break; + case VIDEOSIZE_QCIF: + DBG("Setting size to QCIF\n"); + cam->params.roi.width = STV_IMAGE_QCIF_COLS; + cam->params.roi.height = STV_IMAGE_QCIF_ROWS; + cam->width = STV_IMAGE_QCIF_COLS; + cam->height = STV_IMAGE_QCIF_ROWS; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/****************************************************************************** + * + * configure_sensor + * + *****************************************************************************/ +static int configure_sensor(struct camera_data *cam, + int req_width, int req_height) +{ + int retval; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + retval = config_sensor_410(cam, req_width, req_height); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + retval = config_sensor_500(cam, req_width, req_height); + break; + default: + return -EINVAL; + } + + return retval; +} + +/****************************************************************************** + * + * config_sensor_410 + * + *****************************************************************************/ +static int config_sensor_410(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size; + int image_type; + int width = req_width; + int height = req_height; + + /*** + * Make sure size doesn't exceed CIF. + ***/ + if (width > STV_IMAGE_CIF_COLS) + width = STV_IMAGE_CIF_COLS; + if (height > STV_IMAGE_CIF_ROWS) + height = STV_IMAGE_CIF_ROWS; + + image_size = cpia2_match_video_size(width, height); + + DBG("Config 410: width = %d, height = %d\n", width, height); + DBG("Image size returned is %d\n", image_size); + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + + DBG("After set_vw_size(), width = %d, height = %d\n", + width, height); + if (width <= 176 && height <= 144) { + DBG("image type = VIDEOSIZE_QCIF\n"); + image_type = VIDEOSIZE_QCIF; + } + else if (width <= 320 && height <= 240) { + DBG("image type = VIDEOSIZE_QVGA\n"); + image_type = VIDEOSIZE_QVGA; + } + else { + DBG("image type = VIDEOSIZE_CIF\n"); + image_type = VIDEOSIZE_CIF; + } + } else { + ERR("ConfigSensor410 failed\n"); + return -EINVAL; + } + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + if (image_type == VIDEOSIZE_CIF) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_FORMAT_UFIRST | + CPIA2_VC_VC_FORMAT_SHORTLINE); + } else { + cmd.buffer.registers[i++].value = + (u8) CPIA2_VC_VC_FORMAT_UFIRST; + } + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (image_type == VIDEOSIZE_QCIF) { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + DBG("VC_Clocks (0xc4) should be B\n"); + } + else { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + } else { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + else { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + } + DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value); + + /* Input reqWidth from VC */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_QCIF_COLS / 4); + else + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_CIF_COLS / 4); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 208; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 160; + else + cmd.buffer.registers[i++].value = (u8) 64; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * config_sensor_500(cam) + * + *****************************************************************************/ +static int config_sensor_500(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size = VIDEOSIZE_CIF; + int image_type = VIDEOSIZE_VGA; + int width = req_width; + int height = req_height; + unsigned int device = cam->params.pnp_id.device_type; + + image_size = cpia2_match_video_size(width, height); + + if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS) + image_type = VIDEOSIZE_VGA; + else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS) + image_type = VIDEOSIZE_CIF; + else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS) + image_type = VIDEOSIZE_QVGA; + else + image_type = VIDEOSIZE_QCIF; + + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + } else { + ERR("ConfigSensor500 failed\n"); + return -EINVAL; + } + + DBG("image_size = %d, width = %d, height = %d, type = %d\n", + image_size, width, height, image_type); + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + i = 0; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING; + i++; + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (device == DEVICE_STV_672) { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV3); + } else { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + i++; + + DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value); + + /* Input width from VP */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_VGA_COLS / 4); + else + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_QVGA_COLS / 4); + i++; + DBG("Input width = %d\n", cmd.buffer.registers[i-1].value); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 250; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 125; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 12; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 64; + else + cmd.buffer.registers[i++].value = (u8) 6; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4; + else + cmd.buffer.registers[i++].value = width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4; + else + cmd.buffer.registers[i++].value = height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 36; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 32; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 26; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 21; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * setallproperties + * + * This sets all user changeable properties to the values in cam->params. + *****************************************************************************/ +static int set_all_properties(struct camera_data *cam) +{ + /** + * Don't set target_kb here, it will be set later. + * framerate and user_mode were already set (set_default_user_mode). + **/ + + cpia2_usb_change_streaming_alternate(cam, + cam->params.camera_state.stream_mode); + + cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, cam->params.vp_params.gpio_direction); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, + cam->params.vp_params.gpio_data); + + v4l2_ctrl_handler_setup(&cam->hdl); + + wake_system(cam); + + set_lowlight_boost(cam); + + return 0; +} + +/****************************************************************************** + * + * cpia2_save_camera_state + * + *****************************************************************************/ +void cpia2_save_camera_state(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, + 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0); + /* Don't get framerate or target_kb. Trust the values we already have */ +} + + +/****************************************************************************** + * + * cpia2_set_flicker_mode + * + *****************************************************************************/ +int cpia2_set_flicker_mode(struct camera_data *cam, int mode) +{ + unsigned char cam_reg; + int err = 0; + + if(cam->params.pnp_id.device_type != DEVICE_STV_672) + return -EINVAL; + + /* Set the appropriate bits in FLICKER_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.flicker_control.cam_register; + + switch(mode) { + case NEVER_FLICKER: + cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_60: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_50: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ; + break; + default: + return -EINVAL; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + /* Set the appropriate bits in EXP_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.vp_params.exposure_modes; + + if (mode == NEVER_FLICKER) { + cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } else { + cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, + TRANSFER_WRITE, 1))) + return err; + + switch(mode) { + case NEVER_FLICKER: + case FLICKER_60: + case FLICKER_50: + cam->params.flicker_control.flicker_mode_req = mode; + break; + default: + err = -EINVAL; + } + + return err; +} + +/****************************************************************************** + * + * cpia2_set_property_flip + * + *****************************************************************************/ +void cpia2_set_property_flip(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_property_mirror + * + *****************************************************************************/ +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_gpio + * + *****************************************************************************/ +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting) +{ + int ret; + + /* Set the microport direction (register 0x90, should be defined + * already) to 1 (user output), and set the microport data (0x91) to + * the value in the ioctl argument. + */ + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_VC_MP_DIR_OUTPUT, + 255); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_direction = 255; + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_VC_MP_DIR_OUTPUT, + setting); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_data = setting; + + return 0; +} + +/****************************************************************************** + * + * cpia2_set_fps + * + *****************************************************************************/ +int cpia2_set_fps(struct camera_data *cam, int framerate) +{ + int retval; + + switch(framerate) { + case CPIA2_VP_FRAMERATE_30: + case CPIA2_VP_FRAMERATE_25: + if(cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == + CPIA2_VP_SENSOR_FLAGS_500) { + return -EINVAL; + } + fallthrough; + case CPIA2_VP_FRAMERATE_15: + case CPIA2_VP_FRAMERATE_12_5: + case CPIA2_VP_FRAMERATE_7_5: + case CPIA2_VP_FRAMERATE_6_25: + break; + default: + return -EINVAL; + } + + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + framerate == CPIA2_VP_FRAMERATE_15) + framerate = 0; /* Work around bug in VP4 */ + + retval = cpia2_do_command(cam, + CPIA2_CMD_FRAMERATE_REQ, + TRANSFER_WRITE, + framerate); + + if(retval == 0) + cam->params.vp_params.frame_rate = framerate; + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_brightness + * + *****************************************************************************/ +void cpia2_set_brightness(struct camera_data *cam, unsigned char value) +{ + /*** + * Don't let the register be set to zero - bug in VP4 - flash of full + * brightness + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) + value++; + DBG("Setting brightness to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_contrast + * + *****************************************************************************/ +void cpia2_set_contrast(struct camera_data *cam, unsigned char value) +{ + DBG("Setting contrast to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_saturation + * + *****************************************************************************/ +void cpia2_set_saturation(struct camera_data *cam, unsigned char value) +{ + DBG("Setting saturation to %d (0x%0x)\n", value, value); + cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); +} + +/****************************************************************************** + * + * wake_system + * + *****************************************************************************/ +static void wake_system(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); +} + +/****************************************************************************** + * + * set_lowlight_boost + * + * Valid for STV500 sensor only + *****************************************************************************/ +static void set_lowlight_boost(struct camera_data *cam) +{ + struct cpia2_command cmd; + + if (cam->params.pnp_id.device_type != DEVICE_STV_672 || + cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500) + return; + + cmd.direction = TRANSFER_WRITE; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 3; + cmd.start = CPIA2_VP_RAM_ADDR_H; + + cmd.buffer.block_data[0] = 0; /* High byte of address to write to */ + cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */ + cmd.buffer.block_data[2] = 0; /* High byte of data to write */ + + cpia2_send_command(cam, &cmd); + + if (cam->params.vp_params.lowlight_boost) { + cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */ + } else { + cmd.buffer.block_data[0] = 0x06; + } + cmd.start = CPIA2_VP_RAM_DATA; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + + /* Rehash the VP4 values */ + cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1); +} + +/****************************************************************************** + * + * cpia2_set_format + * + * Assumes that new size is already set in param struct. + *****************************************************************************/ +void cpia2_set_format(struct camera_data *cam) +{ + cam->flush = true; + + cpia2_usb_stream_pause(cam); + + /* reset camera to new size */ + cpia2_set_low_power(cam); + cpia2_reset_camera(cam); + cam->flush = false; + + cpia2_dbg_dump_registers(cam); + + cpia2_usb_stream_resume(cam); +} + +/****************************************************************************** + * + * cpia2_dbg_dump_registers + * + *****************************************************************************/ +void cpia2_dbg_dump_registers(struct camera_data *cam) +{ +#ifdef _CPIA2_DEBUG_ + struct cpia2_command cmd; + + if (!(debugs_on & DEBUG_DUMP_REGS)) + return; + + cmd.direction = TRANSFER_READ; + + /* Start with bank 0 (SYSTEM) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 3; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "System Device Hi = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "System Device Lo = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "System_system control = 0x%X\n", + cmd.buffer.block_data[2]); + + /* Bank 1 (VC) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.start = 0x80; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "ASIC_ID = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "ASIC_REV = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "PW_CONTRL = 0x%X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "WAKEUP = 0x%X\n", + cmd.buffer.block_data[3]); + + cmd.start = 0xA0; /* ST_CTRL */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream ctrl = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA4; /* Stream status */ + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream status = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA8; /* USB status */ + cmd.reg_count = 3; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB_CTRL = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "USB_STRM = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "USB_STATUS = 0x%X\n", + cmd.buffer.block_data[2]); + + cmd.start = 0xAF; /* USB settings */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB settings = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xC0; /* VC stuff */ + cmd.reg_count = 26; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VC Control = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VC Format = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VC Clocks = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VC IHSize = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VC OHSize = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VC OVSize = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VC HCrop = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "VC VCrop = 0x%0X\n", + cmd.buffer.block_data[13]); + printk(KERN_DEBUG "VC HPhase = 0x%0X\n", + cmd.buffer.block_data[14]); + printk(KERN_DEBUG "VC VPhase = 0x%0X\n", + cmd.buffer.block_data[15]); + printk(KERN_DEBUG "VC HIspan = 0x%0X\n", + cmd.buffer.block_data[16]); + printk(KERN_DEBUG "VC VIspan = 0x%0X\n", + cmd.buffer.block_data[17]); + printk(KERN_DEBUG "VC HiCrop = 0x%0X\n", + cmd.buffer.block_data[18]); + printk(KERN_DEBUG "VC ViCrop = 0x%0X\n", + cmd.buffer.block_data[19]); + printk(KERN_DEBUG "VC HiFract = 0x%0X\n", + cmd.buffer.block_data[20]); + printk(KERN_DEBUG "VC ViFract = 0x%0X\n", + cmd.buffer.block_data[21]); + printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n", + cmd.buffer.block_data[22]); + printk(KERN_DEBUG "VC Creep Per = 0x%0X\n", + cmd.buffer.block_data[23]); + printk(KERN_DEBUG "VC User Sq. = 0x%0X\n", + cmd.buffer.block_data[24]); + printk(KERN_DEBUG "VC Target KB = 0x%0X\n", + cmd.buffer.block_data[25]); + + /*** VP ***/ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 14; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Sys State = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Dev Config = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VP RAM Data = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "Do Call = 0x%0X\n", + cmd.buffer.block_data[13]); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.reg_count = 9; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VP White Bal = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP WB thresh = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP Exp Target = 0x%0X\n", + cmd.buffer.block_data[8]); + + cmd.reg_count = 1; + cmd.start = 0x1B; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n", + cmd.buffer.block_data[0]); + } else { + cmd.reg_count = 8 ; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[7]); + + cmd.reg_count = 1; + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n", + cmd.buffer.block_data[0]); + + cmd.reg_count = 4; + cmd.start = 0x3A; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n", + cmd.buffer.block_data[3]); + } +#endif +} + +/****************************************************************************** + * + * reset_camera_struct + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct(struct camera_data *cam) +{ + /*** + * The following parameter values are the defaults from the register map. + ***/ + cam->params.vp_params.lowlight_boost = 0; + + /* FlickerModes */ + cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; + + /* jpeg params */ + cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + cam->params.compression.creep_period = 2; + cam->params.compression.user_squeeze = 20; + cam->params.compression.inhibit_htables = false; + + /* gpio params */ + cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */ + cam->params.vp_params.gpio_data = 0; + + /* Target kb params */ + cam->params.vc_params.quality = 100; + + /*** + * Set Sensor FPS as fast as possible. + ***/ + if(cam->params.pnp_id.device_type == DEVICE_STV_672) { + if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15; + else + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } else { + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } + + /*** + * Set default video mode as large as possible : + * for vga sensor set to vga, for cif sensor set to CIF. + ***/ + if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) { + cam->sensor_type = CPIA2_SENSOR_500; + cam->video_size = VIDEOSIZE_VGA; + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + } else { + cam->sensor_type = CPIA2_SENSOR_410; + cam->video_size = VIDEOSIZE_CIF; + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + } + + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; +} + +/****************************************************************************** + * + * cpia2_init_camera_struct + * + * Deinitialize camera struct + *****************************************************************************/ +void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf) +{ + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +/****************************************************************************** + * + * cpia2_init_camera_struct + * + * Initializes camera struct, does not call reset to fill in defaults. + *****************************************************************************/ +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) +{ + struct camera_data *cam; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + + if (!cam) { + ERR("couldn't kmalloc cpia2 struct\n"); + return NULL; + } + + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } + + mutex_init(&cam->v4l2_lock); + init_waitqueue_head(&cam->wq_stream); + + return cam; +} + +/****************************************************************************** + * + * cpia2_init_camera + * + * Initializes camera. + *****************************************************************************/ +int cpia2_init_camera(struct camera_data *cam) +{ + DBG("Start\n"); + + cam->mmapped = false; + + /* Get sensor and asic types before reset. */ + cpia2_set_high_power(cam); + cpia2_get_version_info(cam); + if (cam->params.version.asic_id != CPIA2_ASIC_672) { + ERR("Device IO error (asicID has incorrect value of 0x%X\n", + cam->params.version.asic_id); + return -ENODEV; + } + + /* Set GPIO direction and data to a safe state. */ + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, 0); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, + TRANSFER_WRITE, 0); + + /* resetting struct requires version info for sensor and asic types */ + reset_camera_struct(cam); + + cpia2_set_low_power(cam); + + DBG("End\n"); + + return 0; +} + +/****************************************************************************** + * + * cpia2_allocate_buffers + * + *****************************************************************************/ +int cpia2_allocate_buffers(struct camera_data *cam) +{ + int i; + + if(!cam->buffers) { + u32 size = cam->num_frames*sizeof(struct framebuf); + cam->buffers = kmalloc(size, GFP_KERNEL); + if(!cam->buffers) { + ERR("couldn't kmalloc frame buffer structures\n"); + return -ENOMEM; + } + } + + if(!cam->frame_buffer) { + cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames); + if (!cam->frame_buffer) { + ERR("couldn't vmalloc frame buffer data area\n"); + kfree(cam->buffers); + cam->buffers = NULL; + return -ENOMEM; + } + } + + for(i=0; inum_frames-1; ++i) { + cam->buffers[i].next = &cam->buffers[i+1]; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + } + cam->buffers[i].next = cam->buffers; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + cam->curbuff = cam->buffers; + cam->workbuff = cam->curbuff->next; + DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff, + cam->workbuff); + return 0; +} + +/****************************************************************************** + * + * cpia2_free_buffers + * + *****************************************************************************/ +void cpia2_free_buffers(struct camera_data *cam) +{ + if(cam->buffers) { + kfree(cam->buffers); + cam->buffers = NULL; + } + if(cam->frame_buffer) { + rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames); + cam->frame_buffer = NULL; + } +} + +/****************************************************************************** + * + * cpia2_read + * + *****************************************************************************/ +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock) +{ + struct framebuf *frame; + + if (!count) + return 0; + + if (!buf) { + ERR("%s: buffer NULL\n",__func__); + return -EINVAL; + } + + if (!cam) { + ERR("%s: Internal error, camera_data NULL!\n",__func__); + return -EINVAL; + } + + if (!cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + /* Copy cam->curbuff in case it changes while we're processing */ + frame = cam->curbuff; + if (noblock && frame->status != FRAME_READY) { + return -EAGAIN; + } + + if (frame->status != FRAME_READY) { + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (frame = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return 0; + } + + /* copy data to user space */ + if (frame->length > count) + return -EFAULT; + if (copy_to_user(buf, frame->data, frame->length)) + return -EFAULT; + + count = frame->length; + + frame->status = FRAME_EMPTY; + + return count; +} + +/****************************************************************************** + * + * cpia2_poll + * + *****************************************************************************/ +__poll_t cpia2_poll(struct camera_data *cam, struct file *filp, + poll_table *wait) +{ + __poll_t status = v4l2_ctrl_poll(filp, wait); + + if ((poll_requested_events(wait) & (EPOLLIN | EPOLLRDNORM)) && + !cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + poll_wait(filp, &cam->wq_stream, wait); + + if (cam->curbuff->status == FRAME_READY) + status |= EPOLLIN | EPOLLRDNORM; + + return status; +} + +/****************************************************************************** + * + * cpia2_remap_buffer + * + *****************************************************************************/ +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) +{ + const char *adr = (const char *)vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long start = (unsigned long) adr; + unsigned long page, pos; + + DBG("mmap offset:%ld size:%ld\n", start_offset, size); + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + if (size > cam->frame_size*cam->num_frames || + (start_offset % cam->frame_size) != 0 || + (start_offset+size > cam->frame_size*cam->num_frames)) + return -EINVAL; + + pos = ((unsigned long) (cam->frame_buffer)) + start_offset; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + cam->mmapped = true; + return 0; +} diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_registers.h b/drivers/staging/media/deprecated/cpia2/cpia2_registers.h new file mode 100644 index 000000000000..8c73812a15c9 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_registers.h @@ -0,0 +1,463 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/**************************************************************************** + * + * Filename: cpia2registers.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Description: + * Definitions for the CPia2 register set + * + ****************************************************************************/ + +#ifndef CPIA2_REGISTER_HEADER +#define CPIA2_REGISTER_HEADER + +/*** + * System register set (Bank 0) + ***/ +#define CPIA2_SYSTEM_DEVICE_HI 0x00 +#define CPIA2_SYSTEM_DEVICE_LO 0x01 + +#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02 +#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00 +#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01 +#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02 +#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80 + +#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04 + +#define CPIA2_SYSTEM_CACHE_CTRL 0x05 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02 + +#define CPIA2_SYSTEM_SERIAL_CTRL 0x06 +#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00 +#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01 +#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02 +#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05 + +#define CPIA2_SYSTEM_SERIAL_DATA 0x07 + +#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08 + +/*** + * I2C addresses for various devices in CPiA2 + ***/ +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A + +#define CPIA2_SYSTEM_SPARE_REG1 0x09 +#define CPIA2_SYSTEM_SPARE_REG2 0x0A +#define CPIA2_SYSTEM_SPARE_REG3 0x0B + +#define CPIA2_SYSTEM_MC_PORT_0 0x0C +#define CPIA2_SYSTEM_MC_PORT_1 0x0D +#define CPIA2_SYSTEM_MC_PORT_2 0x0E +#define CPIA2_SYSTEM_MC_PORT_3 0x0F + +#define CPIA2_SYSTEM_STATUS_PKT 0x20 +#define CPIA2_SYSTEM_STATUS_PKT_END 0x27 + +#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30 +#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31 +#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32 +#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33 + +#define CPIA2_SYSTEM_FW_VERSION_HI 0x34 +#define CPIA2_SYSTEM_FW_VERSION_LO 0x35 + +#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80 +#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10 + +/*** + * VC register set (Bank 1) + ***/ +#define CPIA2_VC_ASIC_ID 0x80 + +#define CPIA2_VC_ASIC_REV 0x81 + +#define CPIA2_VC_PW_CTRL 0x82 +#define CPIA2_VC_PW_CTRL_COLDSTART 0x01 +#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02 +#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04 +#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08 +#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10 +#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20 +#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40 +#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80 + +#define CPIA2_VC_WAKEUP 0x83 +#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01 +#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02 +#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04 +#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08 + +#define CPIA2_VC_CLOCK_CTRL 0x84 +#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01 + +#define CPIA2_VC_INT_ENABLE 0x88 +#define CPIA2_VC_INT_ENABLE_XX_IE 0x01 +#define CPIA2_VC_INT_ENABLE_SW_IE 0x02 +#define CPIA2_VC_INT_ENABLE_VC_IE 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20 + +#define CPIA2_VC_INT_FLAG 0x89 +#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01 +#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02 +#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20 +#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80 + +#define CPIA2_VC_INT_STATE 0x8A +#define CPIA2_VC_INT_STATE_XX_STATE 0x01 +#define CPIA2_VC_INT_STATE_SW_STATE 0x02 + +#define CPIA2_VC_MP_DIR 0x90 +#define CPIA2_VC_MP_DIR_INPUT 0x00 +#define CPIA2_VC_MP_DIR_OUTPUT 0x01 + +#define CPIA2_VC_MP_DATA 0x91 + +#define CPIA2_VC_DP_CTRL 0x98 +#define CPIA2_VC_DP_CTRL_MODE_0 0x00 +#define CPIA2_VC_DP_CTRL_MODE_A 0x01 +#define CPIA2_VC_DP_CTRL_MODE_B 0x02 +#define CPIA2_VC_DP_CTRL_MODE_C 0x03 +#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04 + +#define CPIA2_VC_AD_CTRL 0x99 +#define CPIA2_VC_AD_CTRL_SRC_0 0x00 +#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01 +#define CPIA2_VC_AD_CTRL_SRC_REG 0x02 +#define CPIA2_VC_AD_CTRL_DST_USB 0x00 +#define CPIA2_VC_AD_CTRL_DST_REG 0x04 + +#define CPIA2_VC_AD_TEST_IN 0x9B + +#define CPIA2_VC_AD_TEST_OUT 0x9C + +#define CPIA2_VC_AD_STATUS 0x9D +#define CPIA2_VC_AD_STATUS_EMPTY 0x01 +#define CPIA2_VC_AD_STATUS_FULL 0x02 + +#define CPIA2_VC_DP_DATA 0x9E + +#define CPIA2_VC_ST_CTRL 0xA0 +#define CPIA2_VC_ST_CTRL_SRC_VC 0x00 +#define CPIA2_VC_ST_CTRL_SRC_DP 0x01 +#define CPIA2_VC_ST_CTRL_SRC_REG 0x02 + +#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04 + +#define CPIA2_VC_ST_CTRL_DST_USB 0x00 +#define CPIA2_VC_ST_CTRL_DST_DP 0x08 +#define CPIA2_VC_ST_CTRL_DST_REG 0x10 + +#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20 +#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40 + +#define CPIA2_VC_ST_TEST 0xA1 +#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00 +#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02 + +#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08 + +#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10 + +#define CPIA2_VC_ST_TEST_IN 0xA2 + +#define CPIA2_VC_ST_TEST_OUT 0xA3 + +#define CPIA2_VC_ST_STATUS 0xA4 +#define CPIA2_VC_ST_STATUS_EMPTY 0x01 +#define CPIA2_VC_ST_STATUS_FULL 0x02 + +#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5 + +#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6 + +#define CPIA2_VC_USB_CTRL 0xA8 +#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01 +#define CPIA2_VC_USB_CTRL_CMD_READY 0x02 +#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04 +#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08 +#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10 +#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80 + +#define CPIA2_VC_USB_STRM 0xA9 +#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01 +#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02 +#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04 +#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08 + +#define CPIA2_VC_USB_STATUS 0xAA +#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01 +#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02 +#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04 +#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08 +#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10 +#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20 +#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40 +#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80 + +#define CPIA2_VC_USB_CMDW 0xAB + +#define CPIA2_VC_USB_DATARW 0xAC + +#define CPIA2_VC_USB_INFO 0xAD + +#define CPIA2_VC_USB_CONFIG 0xAE + +#define CPIA2_VC_USB_SETTINGS 0xAF +#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03 +#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C +#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70 + +#define CPIA2_VC_USB_ISOLIM 0xB0 + +#define CPIA2_VC_USB_ISOFAILS 0xB1 + +#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2 + +#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3 + +#define CPIA2_VC_V2W_CTRL 0xB8 +#define CPIA2_VC_V2W_SELECT 0x01 + +#define CPIA2_VC_V2W_SCL 0xB9 + +#define CPIA2_VC_V2W_SDA 0xBA + +#define CPIA2_VC_VC_CTRL 0xC0 +#define CPIA2_VC_VC_CTRL_RUN 0x01 +#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02 +#define CPIA2_VC_VC_CTRL_IDLING 0x04 +#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10 +#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20 +#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40 + +#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1 + +#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2 + +#define CPIA2_VC_VC_FORMAT 0xC3 +#define CPIA2_VC_VC_FORMAT_UFIRST 0x01 +#define CPIA2_VC_VC_FORMAT_MONO 0x02 +#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04 +#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08 +#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10 + +#define CPIA2_VC_VC_CLOCKS 0xC4 +#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03 +#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04 +#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08 +#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00 +#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01 +#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02 +#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03 +#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08 +#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10 + +#define CPIA2_VC_VC_IHSIZE_LO 0xC5 + +#define CPIA2_VC_VC_XLIM_HI 0xC6 + +#define CPIA2_VC_VC_XLIM_LO 0xC7 + +#define CPIA2_VC_VC_YLIM_HI 0xC8 + +#define CPIA2_VC_VC_YLIM_LO 0xC9 + +#define CPIA2_VC_VC_OHSIZE 0xCA + +#define CPIA2_VC_VC_OVSIZE 0xCB + +#define CPIA2_VC_VC_HCROP 0xCC + +#define CPIA2_VC_VC_VCROP 0xCD + +#define CPIA2_VC_VC_HPHASE 0xCE + +#define CPIA2_VC_VC_VPHASE 0xCF + +#define CPIA2_VC_VC_HISPAN 0xD0 + +#define CPIA2_VC_VC_VISPAN 0xD1 + +#define CPIA2_VC_VC_HICROP 0xD2 + +#define CPIA2_VC_VC_VICROP 0xD3 + +#define CPIA2_VC_VC_HFRACT 0xD4 +#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_VFRACT 0xD5 +#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_JPEG_OPT 0xD6 +#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01 +#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02 +#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04 +#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\ + CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE) + + +#define CPIA2_VC_VC_CREEP_PERIOD 0xD7 +#define CPIA2_VC_VC_USER_SQUEEZE 0xD8 +#define CPIA2_VC_VC_TARGET_KB 0xD9 + +#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6 + + +/*** + * VP register set (Bank 2) + ***/ +#define CPIA2_VP_DEVICEH 0 +#define CPIA2_VP_DEVICEL 1 + +#define CPIA2_VP_SYSTEMSTATE 0x02 +#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01 + +#define CPIA2_VP_SYSTEMCTRL 0x03 +#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80 +#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20 +#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10 +#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08 +#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04 +#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02 +#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01 + +#define CPIA2_VP_SENSOR_FLAGS 0x05 +#define CPIA2_VP_SENSOR_FLAGS_404 0x01 +#define CPIA2_VP_SENSOR_FLAGS_407 0x02 +#define CPIA2_VP_SENSOR_FLAGS_409 0x04 +#define CPIA2_VP_SENSOR_FLAGS_410 0x08 +#define CPIA2_VP_SENSOR_FLAGS_500 0x10 + +#define CPIA2_VP_SENSOR_REV 0x06 + +#define CPIA2_VP_DEVICE_CONFIG 0x07 +#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01 + +#define CPIA2_VP_GPIO_DIRECTION 0x08 +#define CPIA2_VP_GPIO_READ 0xFF +#define CPIA2_VP_GPIO_WRITE 0x00 + +#define CPIA2_VP_GPIO_DATA 0x09 + +#define CPIA2_VP_RAM_ADDR_H 0x0A +#define CPIA2_VP_RAM_ADDR_L 0x0B +#define CPIA2_VP_RAM_DATA 0x0C + +#define CPIA2_VP_PATCH_REV 0x0F + +#define CPIA2_VP4_USER_MODE 0x10 +#define CPIA2_VP5_USER_MODE 0x13 +#define CPIA2_VP_USER_MODE_CIF 0x01 +#define CPIA2_VP_USER_MODE_QCIFDS 0x02 +#define CPIA2_VP_USER_MODE_QCIFPTC 0x04 +#define CPIA2_VP_USER_MODE_QVGADS 0x08 +#define CPIA2_VP_USER_MODE_QVGAPTC 0x10 +#define CPIA2_VP_USER_MODE_VGA 0x20 + +#define CPIA2_VP4_FRAMERATE_REQUEST 0x11 +#define CPIA2_VP5_FRAMERATE_REQUEST 0x14 +#define CPIA2_VP_FRAMERATE_60 0x80 +#define CPIA2_VP_FRAMERATE_50 0x40 +#define CPIA2_VP_FRAMERATE_30 0x20 +#define CPIA2_VP_FRAMERATE_25 0x10 +#define CPIA2_VP_FRAMERATE_15 0x08 +#define CPIA2_VP_FRAMERATE_12_5 0x04 +#define CPIA2_VP_FRAMERATE_7_5 0x02 +#define CPIA2_VP_FRAMERATE_6_25 0x01 + +#define CPIA2_VP4_USER_EFFECTS 0x12 +#define CPIA2_VP5_USER_EFFECTS 0x15 +#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01 +#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02 +#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04 +#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only + +/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User + * Effects */ +#define CPIA2_VP_EXPOSURE_MODES 0x15 +#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20 +#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10 + +#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4 +#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5 + +#define CPIA2_VP_FLICKER_MODES 0x1B +#define CPIA2_VP_FLICKER_MODES_50HZ 0x80 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40 +#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20 +#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10 +#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04 + +#define CPIA2_VP_UMISC 0x1D +#define CPIA2_VP_UMISC_FORCE_MONO 0x80 +#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08 +#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02 + +#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34 + +#define CPIA2_VP_INTERPOLATION 0x24 +#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40 +#define CPIA2_VP_INTERPOLATION_HJOG 0x20 +#define CPIA2_VP_INTERPOLATION_VJOG 0x10 + +#define CPIA2_VP_GAMMA 0x25 +#define CPIA2_VP_DEFAULT_GAMMA 0x10 + +#define CPIA2_VP_YRANGE 0x26 + +#define CPIA2_VP_SATURATION 0x27 + +#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58 +#define CPIA2_VP5_MCYRANGE 0x3B //59 +#define CPIA2_VP5_MYCEILING 0x3C //60 +#define CPIA2_VP5_MCUVSATURATION 0x3D //61 + + +#define CPIA2_VP_REHASH_VALUES 0x60 + + +/*** + * Common sensor registers + ***/ +#define CPIA2_SENSOR_DEVICE_H 0x00 +#define CPIA2_SENSOR_DEVICE_L 0x01 + +#define CPIA2_SENSOR_DATA_FORMAT 0x16 +#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08 +#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10 + +#define CPIA2_SENSOR_CR1 0x76 +#define CPIA2_SENSOR_CR1_STAND_BY 0x01 +#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02 +#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04 +#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08 +#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10 +#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20 +#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40 + +#endif diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_usb.c b/drivers/staging/media/deprecated/cpia2/cpia2_usb.c new file mode 100644 index 000000000000..cba03b286473 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_usb.c @@ -0,0 +1,966 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_usb.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + ****************************************************************************/ + +#include +#include +#include +#include + +#include "cpia2.h" + +static int frame_sizes[] = { + 0, // USBIF_CMDONLY + 0, // USBIF_BULK + 128, // USBIF_ISO_1 + 384, // USBIF_ISO_2 + 640, // USBIF_ISO_3 + 768, // USBIF_ISO_4 + 896, // USBIF_ISO_5 + 1023, // USBIF_ISO_6 +}; + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt] + +static void process_frame(struct camera_data *cam); +static void cpia2_usb_complete(struct urb *urb); +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); + +static void free_sbufs(struct camera_data *cam); +static void add_APPn(struct camera_data *cam); +static void add_COM(struct camera_data *cam); +static int submit_urbs(struct camera_data *cam); +static int set_alternate(struct camera_data *cam, unsigned int alt); +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt); + +static const struct usb_device_id cpia2_id_table[] = { + {USB_DEVICE(0x0553, 0x0100)}, + {USB_DEVICE(0x0553, 0x0140)}, + {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */ + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, cpia2_id_table); + +static struct usb_driver cpia2_driver = { + .name = "cpia2", + .probe = cpia2_usb_probe, + .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, + .id_table = cpia2_id_table +}; + + +/****************************************************************************** + * + * process_frame + * + *****************************************************************************/ +static void process_frame(struct camera_data *cam) +{ + static int frame_count; + + unsigned char *inbuff = cam->workbuff->data; + + DBG("Processing frame #%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = cam->workbuff->length; + + if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) { + frame_count++; + } else { + cam->workbuff->status = FRAME_ERROR; + DBG("Start of frame not found\n"); + return; + } + + /*** + * Now the output buffer should have a JPEG image in it. + ***/ + if(!cam->first_image_seen) { + /* Always skip the first image after streaming + * starts. It is almost certainly corrupt. */ + cam->first_image_seen = 1; + cam->workbuff->status = FRAME_EMPTY; + return; + } + if (cam->workbuff->length > 3) { + if(cam->mmapped && + cam->workbuff->length < cam->workbuff->max_length) { + /* No junk in the buffers */ + memset(cam->workbuff->data+cam->workbuff->length, + 0, cam->workbuff->max_length- + cam->workbuff->length); + } + cam->workbuff->max_length = cam->workbuff->length; + cam->workbuff->status = FRAME_READY; + + if(!cam->mmapped && cam->num_frames > 2) { + /* During normal reading, the most recent + * frame will be read. If the current frame + * hasn't started reading yet, it will never + * be read, so mark it empty. If the buffer is + * mmapped, or we have few buffers, we need to + * wait for the user to free the buffer. + * + * NOTE: This is not entirely foolproof with 3 + * buffers, but it would take an EXTREMELY + * overloaded system to cause problems (possible + * image data corruption). Basically, it would + * need to take more time to execute cpia2_read + * than it would for the camera to send + * cam->num_frames-2 frames before problems + * could occur. + */ + cam->curbuff->status = FRAME_EMPTY; + } + cam->curbuff = cam->workbuff; + cam->workbuff = cam->workbuff->next; + DBG("Changed buffers, work:%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + return; + } else { + DBG("Not enough data for an image.\n"); + } + + cam->workbuff->status = FRAME_ERROR; + return; +} + +/****************************************************************************** + * + * add_APPn + * + * Adds a user specified APPn record + *****************************************************************************/ +static void add_APPn(struct camera_data *cam) +{ + if(cam->APP_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->APP_data, cam->APP_len); + cam->workbuff->length += cam->APP_len; + } +} + +/****************************************************************************** + * + * add_COM + * + * Adds a user specified COM record + *****************************************************************************/ +static void add_COM(struct camera_data *cam) +{ + if(cam->COM_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xFE; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->COM_data, cam->COM_len); + cam->workbuff->length += cam->COM_len; + } +} + +/****************************************************************************** + * + * cpia2_usb_complete + * + * callback when incoming packet is received + *****************************************************************************/ +static void cpia2_usb_complete(struct urb *urb) +{ + int i; + unsigned char *cdata; + static bool frame_ready = false; + struct camera_data *cam = (struct camera_data *) urb->context; + + if (urb->status!=0) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + { + DBG("urb->status = %d!\n", urb->status); + } + DBG("Stopping streaming\n"); + return; + } + + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); + return; + } + + /*** + * Packet collater + ***/ + //DBG("Collating %d packets\n", urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + u16 checksum, iso_checksum; + int j; + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + if(cam->workbuff->status == FRAME_READY) { + struct framebuf *ptr; + /* Try to find an available buffer */ + DBG("workbuff full, searching\n"); + for (ptr = cam->workbuff->next; + ptr != cam->workbuff; + ptr = ptr->next) + { + if (ptr->status == FRAME_EMPTY) { + ptr->status = FRAME_READING; + ptr->length = 0; + break; + } + } + if (ptr == cam->workbuff) + break; /* No READING or EMPTY buffers left */ + + cam->workbuff = ptr; + } + + if (cam->workbuff->status == FRAME_EMPTY || + cam->workbuff->status == FRAME_ERROR) { + cam->workbuff->status = FRAME_READING; + cam->workbuff->length = 0; + } + + //DBG(" Packet %d length = %d, status = %d\n", i, n, st); + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) { + LOG("cpia2 data error: [%d] len=%d, status = %d\n", + i, n, st); + if(!ALLOW_CORRUPT) + cam->workbuff->status = FRAME_ERROR; + continue; + } + + if(n<=2) + continue; + + checksum = 0; + for(j=0; jworkbuff->status = FRAME_ERROR; + continue; + } + } + n -= 2; + + if(cam->workbuff->status != FRAME_READING) { + if((0xFF == cdata[0] && 0xD8 == cdata[1]) || + (0xD8 == cdata[0] && 0xFF == cdata[1] && + 0 != cdata[2])) { + /* frame is skipped, but increment total + * frame count anyway */ + cam->frame_count++; + } + DBG("workbuff not reading, status=%d\n", + cam->workbuff->status); + continue; + } + + if (cam->frame_size < cam->workbuff->length + n) { + ERR("buffer overflow! length: %d, n: %d\n", + cam->workbuff->length, n); + cam->workbuff->status = FRAME_ERROR; + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = + cam->workbuff->length; + continue; + } + + if (cam->workbuff->length == 0) { + int data_offset; + if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) { + data_offset = 1; + } else if((0xFF == cdata[0]) && (0xD8 == cdata[1]) + && (0xFF == cdata[2])) { + data_offset = 2; + } else { + DBG("Ignoring packet, not beginning!\n"); + continue; + } + DBG("Start of frame pattern found\n"); + cam->workbuff->ts = ktime_get_ns(); + cam->workbuff->seq = cam->frame_count++; + cam->workbuff->data[0] = 0xFF; + cam->workbuff->data[1] = 0xD8; + cam->workbuff->length = 2; + add_APPn(cam); + add_COM(cam); + memcpy(cam->workbuff->data+cam->workbuff->length, + cdata+data_offset, n-data_offset); + cam->workbuff->length += n-data_offset; + } else if (cam->workbuff->length > 0) { + memcpy(cam->workbuff->data + cam->workbuff->length, + cdata, n); + cam->workbuff->length += n; + } + + if ((cam->workbuff->length >= 3) && + (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) { + frame_ready = true; + cam->workbuff->data[cam->workbuff->length - 1] = 0; + cam->workbuff->length -= 1; + } else if ((cam->workbuff->length >= 2) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) { + frame_ready = true; + } + + if (frame_ready) { + DBG("Workbuff image size = %d\n",cam->workbuff->length); + process_frame(cam); + + frame_ready = false; + + if (waitqueue_active(&cam->wq_stream)) + wake_up_interruptible(&cam->wq_stream); + } + } + + if(cam->streaming) { + /* resubmit */ + urb->dev = cam->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + ERR("%s: usb_submit_urb ret %d!\n", __func__, i); + } +} + +/****************************************************************************** + * + * configure_transfer_mode + * + *****************************************************************************/ +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) +{ + static unsigned char iso_regs[8][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0xB9, 0x00, 0x00, 0x7E}, + {0xB9, 0x00, 0x01, 0x7E}, + {0xB9, 0x00, 0x02, 0x7E}, + {0xB9, 0x00, 0x02, 0xFE}, + {0xB9, 0x00, 0x03, 0x7E}, + {0xB9, 0x00, 0x03, 0xFD} + }; + struct cpia2_command cmd; + unsigned char reg; + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + /*** + * Write the isoc registers according to the alternate selected + ***/ + cmd.direction = TRANSFER_WRITE; + cmd.buffer.block_data[0] = iso_regs[alt][0]; + cmd.buffer.block_data[1] = iso_regs[alt][1]; + cmd.buffer.block_data[2] = iso_regs[alt][2]; + cmd.buffer.block_data[3] = iso_regs[alt][3]; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_ISOLIM; + cmd.reg_count = 4; + cpia2_send_command(cam, &cmd); + + /*** + * Enable relevant streams before starting polling. + * First read USB Stream Config Register. + ***/ + cmd.direction = TRANSFER_READ; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + reg = cmd.buffer.block_data[0]; + + /* Clear iso, bulk, and int */ + reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE | + CPIA2_VC_USB_STRM_ISO_ENABLE | + CPIA2_VC_USB_STRM_INT_ENABLE); + + if (alt == USBIF_BULK) { + DBG("Enabling bulk xfer\n"); + reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */ + cam->xfer_mode = XFER_BULK; + } else if (alt >= USBIF_ISO_1) { + DBG("Enabling ISOC xfer\n"); + reg |= CPIA2_VC_USB_STRM_ISO_ENABLE; + cam->xfer_mode = XFER_ISOC; + } + + cmd.buffer.block_data[0] = reg; + cmd.direction = TRANSFER_WRITE; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cpia2_send_command(cam, &cmd); + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_change_streaming_alternate + * + *****************************************************************************/ +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt) +{ + int ret = 0; + + if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6) + return -EINVAL; + + if(alt == cam->params.camera_state.stream_mode) + return 0; + + cpia2_usb_stream_pause(cam); + + configure_transfer_mode(cam, alt); + + cam->params.camera_state.stream_mode = alt; + + /* Reset the camera to prevent image quality degradation */ + cpia2_reset_camera(cam); + + cpia2_usb_stream_resume(cam); + + return ret; +} + +/****************************************************************************** + * + * set_alternate + * + *****************************************************************************/ +static int set_alternate(struct camera_data *cam, unsigned int alt) +{ + int ret = 0; + + if(alt == cam->cur_alt) + return 0; + + if (cam->cur_alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY); + ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY); + if (ret != 0) + return ret; + } + if (alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt); + ret = usb_set_interface(cam->dev, cam->iface, alt); + if (ret != 0) + return ret; + } + + cam->old_alt = cam->cur_alt; + cam->cur_alt = alt; + + return ret; +} + +/****************************************************************************** + * + * free_sbufs + * + * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL + * are assumed to be allocated. Non-NULL .urb members are also assumed to be + * submitted (and must therefore be killed before they are freed). + *****************************************************************************/ +static void free_sbufs(struct camera_data *cam) +{ + int i; + + for (i = 0; i < NUM_SBUF; i++) { + if(cam->sbuf[i].urb) { + usb_kill_urb(cam->sbuf[i].urb); + usb_free_urb(cam->sbuf[i].urb); + cam->sbuf[i].urb = NULL; + } + if(cam->sbuf[i].data) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + } +} + +/******* +* Convenience functions +*******/ +/**************************************************************************** + * + * write_packet + * + ***************************************************************************/ +static int write_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + unsigned char *buf; + int ret; + + if (!registers || size <= 0) + return -EINVAL; + + buf = kmemdup(registers, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + buf, /* buffer */ + size, + 1000); + + kfree(buf); + return ret; +} + +/**************************************************************************** + * + * read_packet + * + ***************************************************************************/ +static int read_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + unsigned char *buf; + int ret; + + if (!registers || size <= 0) + return -EINVAL; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + request, + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + buf, /* buffer */ + size, + 1000); + + if (ret >= 0) + memcpy(registers, buf, size); + + kfree(buf); + + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_transfer_cmd + * + *****************************************************************************/ +int cpia2_usb_transfer_cmd(struct camera_data *cam, + void *registers, + u8 request, u8 start, u8 count, u8 direction) +{ + int err = 0; + struct usb_device *udev = cam->dev; + + if (!udev) { + ERR("%s: Internal driver error: udev is NULL\n", __func__); + return -EINVAL; + } + + if (!registers) { + ERR("%s: Internal driver error: register array is NULL\n", __func__); + return -EINVAL; + } + + if (direction == TRANSFER_READ) { + err = read_packet(udev, request, (u8 *)registers, start, count); + if (err > 0) + err = 0; + } else if (direction == TRANSFER_WRITE) { + err =write_packet(udev, request, (u8 *)registers, start, count); + if (err < 0) { + LOG("Control message failed, err val = %d\n", err); + LOG("Message: request = 0x%0X, start = 0x%0X\n", + request, start); + LOG("Message: count = %d, register[0] = 0x%0X\n", + count, ((unsigned char *) registers)[0]); + } else + err=0; + } else { + LOG("Unexpected first byte of direction: %d\n", + direction); + return -EINVAL; + } + + if(err != 0) + LOG("Unexpected error: %d\n", err); + return err; +} + + +/****************************************************************************** + * + * submit_urbs + * + *****************************************************************************/ +static int submit_urbs(struct camera_data *cam) +{ + struct urb *urb; + int fx, err, i, j; + + for(i=0; isbuf[i].data) + continue; + cam->sbuf[i].data = + kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC, + GFP_KERNEL); + if (!cam->sbuf[i].data) { + while (--i >= 0) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + return -ENOMEM; + } + } + + /* We double buffer the Isoc lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + for(i=0; isbuf[i].urb) { + continue; + } + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + for (j = 0; j < i; j++) + usb_free_urb(cam->sbuf[j].urb); + for (j = 0; j < NUM_SBUF; j++) { + kfree(cam->sbuf[j].data); + cam->sbuf[j].data = NULL; + } + return -ENOMEM; + } + + cam->sbuf[i].urb = urb; + urb->dev = cam->dev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = cam->sbuf[i].data; + urb->complete = cpia2_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = + FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = + FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + } + + + /* Queue the ISO urbs, and resubmit in the completion handler */ + for(i=0; isbuf[i].urb, GFP_KERNEL); + if (err) { + ERR("usb_submit_urb[%d]() = %d\n", i, err); + return err; + } + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_stream_start + * + *****************************************************************************/ +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate) +{ + int ret; + int old_alt; + + if(cam->streaming) + return 0; + + if (cam->flush) { + int i; + DBG("Flushing buffers\n"); + for(i=0; inum_frames; ++i) { + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + } + cam->curbuff = &cam->buffers[0]; + cam->workbuff = cam->curbuff->next; + cam->flush = false; + } + + old_alt = cam->params.camera_state.stream_mode; + cam->params.camera_state.stream_mode = 0; + ret = cpia2_usb_change_streaming_alternate(cam, alternate); + if (ret < 0) { + int ret2; + ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret); + cam->params.camera_state.stream_mode = old_alt; + ret2 = set_alternate(cam, USBIF_CMDONLY); + if (ret2 < 0) { + ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already failed. Then tried to call set_alternate(USBIF_CMDONLY) = %d.\n", + alternate, ret, ret2); + } + } else { + cam->frame_count = 0; + cam->streaming = 1; + ret = cpia2_usb_stream_resume(cam); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_pause + * + *****************************************************************************/ +int cpia2_usb_stream_pause(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_resume + * + *****************************************************************************/ +int cpia2_usb_stream_resume(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + cam->first_image_seen = 0; + ret = set_alternate(cam, cam->params.camera_state.stream_mode); + if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); + ret = submit_urbs(cam); + } + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_stop + * + *****************************************************************************/ +int cpia2_usb_stream_stop(struct camera_data *cam) +{ + int ret; + + ret = cpia2_usb_stream_pause(cam); + cam->streaming = 0; + configure_transfer_mode(cam, 0); + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_probe + * + * Probe and initialize. + *****************************************************************************/ +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_interface_descriptor *interface; + struct camera_data *cam; + int ret; + + /* A multi-config CPiA2 camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + interface = &intf->cur_altsetting->desc; + + /* If we get to this point, we found a CPiA2 camera */ + LOG("CPiA2 USB camera found\n"); + + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) + return -ENOMEM; + + cam->dev = udev; + cam->iface = interface->bInterfaceNumber; + + ret = set_alternate(cam, USBIF_CMDONLY); + if (ret < 0) { + ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret); + goto alt_err; + } + + + if((ret = cpia2_init_camera(cam)) < 0) { + ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); + goto alt_err; + } + LOG(" CPiA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmware_revision_hi, + cam->params.version.firmware_revision_lo, + cam->params.version.asic_id, + cam->params.version.asic_rev); + LOG(" CPiA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnp_id.vendor, + cam->params.pnp_id.product, + cam->params.pnp_id.device_revision); + LOG(" SensorID: %d.(version %d)\n", + cam->params.version.sensor_flags, + cam->params.version.sensor_rev); + + usb_set_intfdata(intf, cam); + + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + goto alt_err; + } + + return 0; + +alt_err: + cpia2_deinit_camera_struct(cam, intf); + return ret; +} + +/****************************************************************************** + * + * cpia2_disconnect + * + *****************************************************************************/ +static void cpia2_usb_disconnect(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + DBG("Stopping stream\n"); + cpia2_usb_stream_stop(cam); + + mutex_lock(&cam->v4l2_lock); + DBG("Unregistering camera\n"); + cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + + if(cam->buffers) { + DBG("Wakeup waiting processes\n"); + cam->curbuff->status = FRAME_READY; + cam->curbuff->length = 0; + wake_up_interruptible(&cam->wq_stream); + } + + v4l2_device_put(&cam->v4l2_dev); + + LOG("CPiA2 camera disconnected.\n"); +} + +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "going into suspend..\n"); + return 0; +} + +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} + +/****************************************************************************** + * + * usb_cpia2_init + * + *****************************************************************************/ +int cpia2_usb_init(void) +{ + return usb_register(&cpia2_driver); +} + +/****************************************************************************** + * + * usb_cpia_cleanup + * + *****************************************************************************/ +void cpia2_usb_cleanup(void) +{ + schedule_timeout(2 * HZ); + usb_deregister(&cpia2_driver); +} diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c b/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c new file mode 100644 index 000000000000..926ecfc9b64a --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c @@ -0,0 +1,1226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_v4l.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * Copyright 2001,2005, Scott J. Bertin + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox + ****************************************************************************/ + +#define CPIA_VERSION "3.0.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia2.h" + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +static int buffer_size = 68 * 1024; +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); + +static int num_buffers = 3; +module_param(num_buffers, int, 0); +MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-" + __stringify(VIDEO_MAX_FRAME) ", default 3)"); + +static int alternate = DEFAULT_ALT; +module_param(alternate, int, 0); +MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" + __stringify(USBIF_ISO_6) ", default " + __stringify(DEFAULT_ALT) ")"); + +static int flicker_mode; +module_param(flicker_mode, int, 0); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); + +MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); +MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CPIA_VERSION); + +#define ABOUT "V4L-Driver for Vision CPiA2 based cameras" +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) + +/****************************************************************************** + * + * cpia2_open + * + *****************************************************************************/ +static int cpia2_open(struct file *file) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = v4l2_fh_open(file); + if (retval) + goto open_unlock; + + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); + retval = -ENOMEM; + goto open_unlock; + } + + /* reset the camera */ + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); + retval = -EIO; + goto open_unlock; + } + + cam->APP_len = 0; + cam->COM_len = 0; + } + + cpia2_dbg_dump_registers(cam); +open_unlock: + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * cpia2_close + * + *****************************************************************************/ +static int cpia2_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct camera_data *cam = video_get_drvdata(dev); + + mutex_lock(&cam->v4l2_lock); + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { + cpia2_usb_stream_stop(cam); + + /* save camera state for later open */ + cpia2_save_camera_state(cam); + + cpia2_set_low_power(cam); + cpia2_free_buffers(cam); + } + + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; + cam->mmapped = 0; + } + mutex_unlock(&cam->v4l2_lock); + return v4l2_fh_release(file); +} + +/****************************************************************************** + * + * cpia2_v4l_read + * + *****************************************************************************/ +static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + struct camera_data *cam = video_drvdata(file); + int noblock = file->f_flags & O_NONBLOCK; + ssize_t ret; + + if (!cam) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + ret = cpia2_read(cam, buf, count, noblock); + mutex_unlock(&cam->v4l2_lock); + return ret; +} + +/****************************************************************************** + * + * cpia2_v4l_poll + * + *****************************************************************************/ +static __poll_t cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct camera_data *cam = video_drvdata(filp); + __poll_t res; + + mutex_lock(&cam->v4l2_lock); + res = cpia2_poll(cam, filp, wait); + mutex_unlock(&cam->v4l2_lock); + return res; +} + +static int sync(struct camera_data *cam, int frame_nr) +{ + struct framebuf *frame = &cam->buffers[frame_nr]; + + while (1) { + if (frame->status == FRAME_READY) + return 0; + + if (!cam->streaming) { + frame->status = FRAME_READY; + frame->length = 0; + return 0; + } + + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !cam->streaming || + frame->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + } +} + +/****************************************************************************** + * + * ioctl_querycap + * + * V4L2 device capabilities + * + *****************************************************************************/ + +static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) +{ + struct camera_data *cam = video_drvdata(file); + + strscpy(vc->driver, "cpia2", sizeof(vc->driver)); + + if (cam->params.pnp_id.product == 0x151) + strscpy(vc->card, "QX5 Microscope", sizeof(vc->card)); + else + strscpy(vc->card, "CPiA2 Camera", sizeof(vc->card)); + switch (cam->params.pnp_id.device_type) { + case DEVICE_STV_672: + strcat(vc->card, " (672/"); + break; + case DEVICE_STV_676: + strcat(vc->card, " (676/"); + break; + default: + strcat(vc->card, " (XXX/"); + break; + } + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + strcat(vc->card, "404)"); + break; + case CPIA2_VP_SENSOR_FLAGS_407: + strcat(vc->card, "407)"); + break; + case CPIA2_VP_SENSOR_FLAGS_409: + strcat(vc->card, "409)"); + break; + case CPIA2_VP_SENSOR_FLAGS_410: + strcat(vc->card, "410)"); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + strcat(vc->card, "500)"); + break; + default: + strcat(vc->card, "XXX)"); + break; + } + + if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) < 0) + memset(vc->bus_info, 0, sizeof(vc->bus_info)); + return 0; +} + +/****************************************************************************** + * + * ioctl_input + * + * V4L2 input get/set/enumerate + * + *****************************************************************************/ + +static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index) + return -EINVAL; + strscpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cpia2_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +/****************************************************************************** + * + * ioctl_enum_fmt + * + * V4L2 format enumerate + * + *****************************************************************************/ + +static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; + + if (f->index == 0) + f->pixelformat = V4L2_PIX_FMT_MJPEG; + else + f->pixelformat = V4L2_PIX_FMT_JPEG; + return 0; +} + +/****************************************************************************** + * + * ioctl_try_fmt + * + * V4L2 format try + * + *****************************************************************************/ + +static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + + switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) { + case VIDEOSIZE_VGA: + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + break; + case VIDEOSIZE_CIF: + f->fmt.pix.width = 352; + f->fmt.pix.height = 288; + break; + case VIDEOSIZE_QVGA: + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + break; + case VIDEOSIZE_288_216: + f->fmt.pix.width = 288; + f->fmt.pix.height = 216; + break; + case VIDEOSIZE_256_192: + f->fmt.pix.width = 256; + f->fmt.pix.height = 192; + break; + case VIDEOSIZE_224_168: + f->fmt.pix.width = 224; + f->fmt.pix.height = 168; + break; + case VIDEOSIZE_192_144: + f->fmt.pix.width = 192; + f->fmt.pix.height = 144; + break; + case VIDEOSIZE_QCIF: + default: + f->fmt.pix.width = 176; + f->fmt.pix.height = 144; + break; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_set_fmt + * + * V4L2 format set + * + *****************************************************************************/ + +static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + int err, frame; + + err = cpia2_try_fmt_vid_cap(file, _fh, f); + if (err != 0) + return err; + + cam->pixelformat = f->fmt.pix.pixelformat; + + /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle + * the missing Huffman table properly. + */ + cam->params.compression.inhibit_htables = 0; + /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/ + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + DBG("Requested width = %d, height = %d\n", + f->fmt.pix.width, f->fmt.pix.height); + if (f->fmt.pix.width != cam->width || + f->fmt.pix.height != cam->height) { + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + cam->params.roi.width = f->fmt.pix.width; + cam->params.roi.height = f->fmt.pix.height; + cpia2_set_format(cam); + } + + for (frame = 0; frame < cam->num_frames; ++frame) { + if (cam->buffers[frame].status == FRAME_READING) + if ((err = sync(cam, frame)) < 0) + return err; + + cam->buffers[frame].status = FRAME_EMPTY; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_get_fmt + * + * V4L2 format get + * + *****************************************************************************/ + +static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.pixelformat = cam->pixelformat; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + + return 0; +} + +/****************************************************************************** + * + * ioctl_cropcap + * + * V4L2 query cropping capabilities + * NOTE: cropping is currently disabled + * + *****************************************************************************/ + +static int cpia2_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct camera_data *cam = video_drvdata(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = cam->width; + s->r.height = cam->height; + break; + default: + return -EINVAL; + } + return 0; +} + +struct framerate_info { + int value; + struct v4l2_fract period; +}; + +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + int i; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; + break; + } + return 0; +} + +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; + + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} + +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; + +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; + + return 0; +} + +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; + + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; + return 0; +} + +/****************************************************************************** + * + * ioctl_s_ctrl + * + * V4L2 set the value of a control variable + * + *****************************************************************************/ + +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; + + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cpia2_set_brightness(cam, ctrl->val); + break; + case V4L2_CID_CONTRAST: + cpia2_set_contrast(cam, ctrl->val); + break; + case V4L2_CID_SATURATION: + cpia2_set_saturation(cam, ctrl->val); + break; + case V4L2_CID_HFLIP: + cpia2_set_property_mirror(cam, ctrl->val); + break; + case V4L2_CID_VFLIP: + cpia2_set_property_flip(cam, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; + break; + case CPIA2_CID_USB_ALT: + cam->params.camera_state.stream_mode = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_g_jpegcomp + * + * V4L2 get the JPEG compression parameters + * + *****************************************************************************/ + +static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + memset(parms, 0, sizeof(*parms)); + + parms->quality = 80; // TODO: Can this be made meaningful? + + parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + if (!cam->params.compression.inhibit_htables) + parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT; + + parms->APPn = cam->APPn; + parms->APP_len = cam->APP_len; + if (cam->APP_len > 0) { + memcpy(parms->APP_data, cam->APP_data, cam->APP_len); + parms->jpeg_markers |= V4L2_JPEG_MARKER_APP; + } + + parms->COM_len = cam->COM_len; + if (cam->COM_len > 0) { + memcpy(parms->COM_data, cam->COM_data, cam->COM_len); + parms->jpeg_markers |= JPEG_MARKER_COM; + } + + DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + return 0; +} + +/****************************************************************************** + * + * ioctl_s_jpegcomp + * + * V4L2 set the JPEG compression parameters + * NOTE: quality and some jpeg_markers are ignored. + * + *****************************************************************************/ + +static int cpia2_s_jpegcomp(struct file *file, void *fh, + const struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + cam->params.compression.inhibit_htables = + !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + + if (parms->APP_len != 0) { + if (parms->APP_len > 0 && + parms->APP_len <= sizeof(cam->APP_data) && + parms->APPn >= 0 && parms->APPn <= 15) { + cam->APPn = parms->APPn; + cam->APP_len = parms->APP_len; + memcpy(cam->APP_data, parms->APP_data, parms->APP_len); + } else { + LOG("Bad APPn Params n=%d len=%d\n", + parms->APPn, parms->APP_len); + return -EINVAL; + } + } else { + cam->APP_len = 0; + } + + if (parms->COM_len != 0) { + if (parms->COM_len > 0 && + parms->COM_len <= sizeof(cam->COM_data)) { + cam->COM_len = parms->COM_len; + memcpy(cam->COM_data, parms->COM_data, parms->COM_len); + } else { + LOG("Bad COM_len=%d\n", parms->COM_len); + return -EINVAL; + } + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_reqbufs + * + * V4L2 Initiate memory mapping. + * NOTE: The user's request is ignored. For now the buffers are fixed. + * + *****************************************************************************/ + +static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) +{ + struct camera_data *cam = video_drvdata(file); + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames); + req->count = cam->num_frames; + memset(&req->reserved, 0, sizeof(req->reserved)); + + return 0; +} + +/****************************************************************************** + * + * ioctl_querybuf + * + * V4L2 Query memory buffer status. + * + *****************************************************************************/ + +static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->index >= cam->num_frames) + return -EINVAL; + + buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; + buf->length = cam->frame_size; + + buf->memory = V4L2_MEMORY_MMAP; + + if (cam->mmapped) + buf->flags = V4L2_BUF_FLAG_MAPPED; + else + buf->flags = 0; + + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + switch (cam->buffers[buf->index].status) { + case FRAME_EMPTY: + case FRAME_ERROR: + case FRAME_READING: + buf->bytesused = 0; + buf->flags = V4L2_BUF_FLAG_QUEUED; + break; + case FRAME_READY: + buf->bytesused = cam->buffers[buf->index].length; + v4l2_buffer_set_timestamp(buf, cam->buffers[buf->index].ts); + buf->sequence = cam->buffers[buf->index].seq; + buf->flags = V4L2_BUF_FLAG_DONE; + break; + } + + DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n", + buf->index, buf->m.offset, buf->flags, buf->sequence, + buf->bytesused); + + return 0; +} + +/****************************************************************************** + * + * ioctl_qbuf + * + * V4L2 User is freeing buffer + * + *****************************************************************************/ + +static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP || + buf->index >= cam->num_frames) + return -EINVAL; + + DBG("QBUF #%d\n", buf->index); + + if (cam->buffers[buf->index].status == FRAME_READY) + cam->buffers[buf->index].status = FRAME_EMPTY; + + return 0; +} + +/****************************************************************************** + * + * find_earliest_filled_buffer + * + * Helper for ioctl_dqbuf. Find the next ready buffer. + * + *****************************************************************************/ + +static int find_earliest_filled_buffer(struct camera_data *cam) +{ + int i; + int found = -1; + + for (i = 0; i < cam->num_frames; i++) { + if (cam->buffers[i].status == FRAME_READY) { + if (found < 0) { + found = i; + } else { + /* find which buffer is earlier */ + if (cam->buffers[i].ts < cam->buffers[found].ts) + found = i; + } + } + } + return found; +} + +/****************************************************************************** + * + * ioctl_dqbuf + * + * V4L2 User is asking for a filled buffer. + * + *****************************************************************************/ + +static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + int frame; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + frame = find_earliest_filled_buffer(cam); + + if (frame < 0 && file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (frame < 0) { + /* Wait for a frame to become available */ + struct framebuf *cb = cam->curbuff; + + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (cb = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + frame = cb->num; + } + + buf->index = frame; + buf->bytesused = cam->buffers[buf->index].length; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, 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; + buf->reserved2 = 0; + buf->request_fd = 0; + memset(&buf->timecode, 0, sizeof(buf->timecode)); + + DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index, + cam->buffers[buf->index].status, buf->sequence, buf->bytesused); + + return 0; +} + +static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; +} + +static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_mmap + * + *****************************************************************************/ +static int cpia2_mmap(struct file *file, struct vm_area_struct *area) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = cpia2_remap_buffer(cam, area); + + if (!retval) + cam->stream_fh = file->private_data; + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * reset_camera_struct_v4l + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct_v4l(struct camera_data *cam) +{ + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; + + cam->frame_size = buffer_size; + cam->num_frames = num_buffers; + + /* Flicker modes */ + cam->params.flicker_control.flicker_mode_req = flicker_mode; + + /* stream modes */ + cam->params.camera_state.stream_mode = alternate; + + cam->pixelformat = V4L2_PIX_FMT_JPEG; +} + +static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { + .vidioc_querycap = cpia2_querycap, + .vidioc_enum_input = cpia2_enum_input, + .vidioc_g_input = cpia2_g_input, + .vidioc_s_input = cpia2_s_input, + .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, + .vidioc_g_jpegcomp = cpia2_g_jpegcomp, + .vidioc_s_jpegcomp = cpia2_s_jpegcomp, + .vidioc_g_selection = cpia2_g_selection, + .vidioc_reqbufs = cpia2_reqbufs, + .vidioc_querybuf = cpia2_querybuf, + .vidioc_qbuf = cpia2_qbuf, + .vidioc_dqbuf = cpia2_dqbuf, + .vidioc_streamon = cpia2_streamon, + .vidioc_streamoff = cpia2_streamoff, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/*** + * The v4l video device structure initialized for this device + ***/ +static const struct v4l2_file_operations cpia2_fops = { + .owner = THIS_MODULE, + .open = cpia2_open, + .release = cpia2_close, + .read = cpia2_v4l_read, + .poll = cpia2_v4l_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = cpia2_mmap, +}; + +static const struct video_device cpia2_template = { + /* I could not find any place for the old .initialize initializer?? */ + .name = "CPiA2 Camera", + .fops = &cpia2_fops, + .ioctl_ops = &cpia2_ioctl_ops, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, +}; + +/****************************************************************************** + * + * cpia2_register_camera + * + *****************************************************************************/ +int cpia2_register_camera(struct camera_data *cam) +{ + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; + + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, + 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, + 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + reset_camera_struct_v4l(cam); + + /* register v4l device */ + if (video_register_device(&cam->vdev, VFL_TYPE_VIDEO, video_nr) < 0) { + ERR("video_register_device failed\n"); + return -ENODEV; + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_unregister_camera + * + *****************************************************************************/ +void cpia2_unregister_camera(struct camera_data *cam) +{ + video_unregister_device(&cam->vdev); +} + +/****************************************************************************** + * + * check_parameters + * + * Make sure that all user-supplied parameters are sensible + *****************************************************************************/ +static void __init check_parameters(void) +{ + if (buffer_size < PAGE_SIZE) { + buffer_size = PAGE_SIZE; + LOG("buffer_size too small, setting to %d\n", buffer_size); + } else if (buffer_size > 1024 * 1024) { + /* arbitrary upper limiit */ + buffer_size = 1024 * 1024; + LOG("buffer_size ridiculously large, setting to %d\n", + buffer_size); + } else { + buffer_size += PAGE_SIZE - 1; + buffer_size &= ~(PAGE_SIZE - 1); + } + + if (num_buffers < 1) { + num_buffers = 1; + LOG("num_buffers too small, setting to %d\n", num_buffers); + } else if (num_buffers > VIDEO_MAX_FRAME) { + num_buffers = VIDEO_MAX_FRAME; + LOG("num_buffers too large, setting to %d\n", num_buffers); + } + + if (alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) { + alternate = DEFAULT_ALT; + LOG("alternate specified is invalid, using %d\n", alternate); + } + + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; + LOG("Flicker mode specified is invalid, using %d\n", + flicker_mode); + } + + DBG("Using %d buffers, each %d bytes, alternate=%d\n", + num_buffers, buffer_size, alternate); +} + +/************ Module Stuff ***************/ + +/****************************************************************************** + * + * cpia2_init/module_init + * + *****************************************************************************/ +static int __init cpia2_init(void) +{ + LOG("%s v%s\n", + ABOUT, CPIA_VERSION); + check_parameters(); + return cpia2_usb_init(); +} + +/****************************************************************************** + * + * cpia2_exit/module_exit + * + *****************************************************************************/ +static void __exit cpia2_exit(void) +{ + cpia2_usb_cleanup(); + schedule_timeout(2 * HZ); +} + +module_init(cpia2_init); +module_exit(cpia2_exit); -- cgit v1.3-14-g43fede From 9a97cc155cc75571db72adf6992d11aa1de0c83a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:43 +0200 Subject: media: meye: deprecate this driver Deprecate the meye driver. This driver does not use the vb2 framework for video streaming, instead it implements its own version. We want to get rid of these old drivers, so deprecated it for future removal. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/pci/Kconfig | 1 - drivers/media/pci/Makefile | 1 - drivers/media/pci/meye/Kconfig | 16 - drivers/media/pci/meye/Makefile | 2 - drivers/media/pci/meye/meye.c | 1814 ------------------------ drivers/media/pci/meye/meye.h | 311 ---- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/deprecated/meye/Kconfig | 19 + drivers/staging/media/deprecated/meye/Makefile | 2 + drivers/staging/media/deprecated/meye/TODO | 6 + drivers/staging/media/deprecated/meye/meye.c | 1814 ++++++++++++++++++++++++ drivers/staging/media/deprecated/meye/meye.h | 311 ++++ 14 files changed, 2155 insertions(+), 2146 deletions(-) delete mode 100644 drivers/media/pci/meye/Kconfig delete mode 100644 drivers/media/pci/meye/Makefile delete mode 100644 drivers/media/pci/meye/meye.c delete mode 100644 drivers/media/pci/meye/meye.h create mode 100644 drivers/staging/media/deprecated/meye/Kconfig create mode 100644 drivers/staging/media/deprecated/meye/Makefile create mode 100644 drivers/staging/media/deprecated/meye/TODO create mode 100644 drivers/staging/media/deprecated/meye/meye.c create mode 100644 drivers/staging/media/deprecated/meye/meye.h diff --git a/MAINTAINERS b/MAINTAINERS index 16ee869e6015..47b9118ee992 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13775,7 +13775,7 @@ MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER S: Orphan W: http://popies.net/meye/ F: Documentation/userspace-api/media/drivers/meye* -F: drivers/media/pci/meye/ +F: drivers/staging/media/deprecated/meye/ F: include/uapi/linux/meye.h MOTORCOMM PHY DRIVER diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 7a229dddadaf..480194543d05 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -13,7 +13,6 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" -source "drivers/media/pci/meye/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 00d740b953d5..8bed619b7130 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ -obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig deleted file mode 100644 index 3e69b66f1a5b..000000000000 --- a/drivers/media/pci/meye/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_MEYE - tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && VIDEO_DEV - depends on SONY_LAPTOP - depends on X86 || COMPILE_TEST - help - This is the video4linux driver for the Motion Eye camera found - in the Vaio Picturebook laptops. Please read the material in - for more information. - - If you say Y or M here, you need to say Y or M to "Sony Laptop - Extras" in the misc device section. - - To compile this driver as a module, choose M here: the - module will be called meye. diff --git a/drivers/media/pci/meye/Makefile b/drivers/media/pci/meye/Makefile deleted file mode 100644 index 36f1f86f0d58..000000000000 --- a/drivers/media/pci/meye/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_MEYE) += meye.o diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c deleted file mode 100644 index 5d87efd9b95c..000000000000 --- a/drivers/media/pci/meye/meye.c +++ /dev/null @@ -1,1814 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Motion Eye video4linux driver for Sony Vaio PictureBook - * - * Copyright (C) 2001-2004 Stelian Pop - * - * Copyright (C) 2001-2002 Alcôve - * - * Copyright (C) 2000 Andrew Tridgell - * - * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. - * - * Some parts borrowed from various video4linux drivers, especially - * bttv-driver.c and zoran.c, see original files for credits. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "meye.h" -#include - -MODULE_AUTHOR("Stelian Pop "); -MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MEYE_DRIVER_VERSION); - -/* number of grab buffers */ -static unsigned int gbuffers = 2; -module_param(gbuffers, int, 0444); -MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)"); - -/* size of a grab buffer */ -static unsigned int gbufsize = MEYE_MAX_BUFSIZE; -module_param(gbufsize, int, 0444); -MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400 (will be rounded up to a page multiple)"); - -/* /dev/videoX registration number */ -static int video_nr = -1; -module_param(video_nr, int, 0444); -MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); - -/* driver structure - only one possible */ -static struct meye meye; - -/****************************************************************************/ -/* Memory allocation routines (stolen from bttv-driver.c) */ -/****************************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (mem) { - memset(mem, 0, size); - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr; - - if (mem) { - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); - } -} - -/* - * return a page table pointing to N pages of locked memory - * - * NOTE: The meye device expects DMA addresses on 32 bits, we build - * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. - */ -static int ptable_alloc(void) -{ - u32 *pt; - int i; - - memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); - - /* give only 32 bit DMA addresses */ - if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32))) - return -1; - - meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - &meye.mchip_dmahandle, - GFP_KERNEL); - if (!meye.mchip_ptable_toc) { - meye.mchip_dmahandle = 0; - return -1; - } - - pt = meye.mchip_ptable_toc; - for (i = 0; i < MCHIP_NB_PAGES; i++) { - dma_addr_t dma; - meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - &dma, - GFP_KERNEL); - if (!meye.mchip_ptable[i]) { - int j; - pt = meye.mchip_ptable_toc; - for (j = 0; j < i; ++j) { - dma = (dma_addr_t) *pt; - dma_free_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - meye.mchip_ptable[j], dma); - pt++; - } - dma_free_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - meye.mchip_ptable_toc, - meye.mchip_dmahandle); - meye.mchip_ptable_toc = NULL; - meye.mchip_dmahandle = 0; - return -1; - } - *pt = (u32) dma; - pt++; - } - return 0; -} - -static void ptable_free(void) -{ - u32 *pt; - int i; - - pt = meye.mchip_ptable_toc; - for (i = 0; i < MCHIP_NB_PAGES; i++) { - dma_addr_t dma = (dma_addr_t) *pt; - if (meye.mchip_ptable[i]) - dma_free_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - meye.mchip_ptable[i], dma); - pt++; - } - - if (meye.mchip_ptable_toc) - dma_free_coherent(&meye.mchip_dev->dev, - PAGE_SIZE, - meye.mchip_ptable_toc, - meye.mchip_dmahandle); - - memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); - meye.mchip_ptable_toc = NULL; - meye.mchip_dmahandle = 0; -} - -/* copy data from ptable into buf */ -static void ptable_copy(u8 *buf, int start, int size, int pt_pages) -{ - int i; - - for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { - memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); - if (start >= pt_pages) - start = 0; - } - memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); -} - -/****************************************************************************/ -/* JPEG tables at different qualities to load into the VRJ chip */ -/****************************************************************************/ - -/* return a set of quantisation tables based on a quality from 1 to 10 */ -static u16 *jpeg_quantisation_tables(int *length, int quality) -{ - static u16 jpeg_tables[][70] = { { - 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, - 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, - }, - { - 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, - 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, - 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, - }, - { - 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, - 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, - 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, - 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, - 0xe6ff, 0xfffd, 0xfff8, - 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, - 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, - 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, - 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, - 0xf8f8, 0xf8f8, 0xfff8, - }, - { - 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, - 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, - 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, - 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, - 0x99c7, 0xaba8, 0xffa4, - 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, - 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, - 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, - 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, - 0xa4a4, 0xa4a4, 0xffa4, - }, - { - 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, - 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, - 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, - 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, - 0x7396, 0x817e, 0xff7c, - 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, - 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, - 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, - 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, - 0x7c7c, 0x7c7c, 0xff7c, - }, - { - 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, - 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, - 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, - 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, - 0x5c78, 0x6765, 0xff63, - 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, - 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, - 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, - 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, - 0x6363, 0x6363, 0xff63, - }, - { - 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, - 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, - 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, - 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, - 0x4a60, 0x5251, 0xff4f, - 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, - 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, - 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, - 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, - 0x4f4f, 0x4f4f, 0xff4f, - }, - { - 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, - 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, - 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, - 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, - 0x3748, 0x3e3d, 0xff3b, - 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, - 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, - 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, - 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, - 0x3b3b, 0x3b3b, 0xff3b, - }, - { - 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, - 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, - 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, - 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, - 0x2530, 0x2928, 0xff28, - 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, - 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, - 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, - 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, - 0x2828, 0x2828, 0xff28, - }, - { - 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, - 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, - 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, - 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, - 0x1218, 0x1514, 0xff14, - 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, - 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, - 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, - 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, - 0x1414, 0x1414, 0xff14, - }, - { - 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0xff01, - 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0101, 0x0101, 0xff01, - } }; - - if (quality < 0 || quality > 10) { - printk(KERN_WARNING - "meye: invalid quality level %d - using 8\n", quality); - quality = 8; - } - - *length = ARRAY_SIZE(jpeg_tables[quality]); - return jpeg_tables[quality]; -} - -/* return a generic set of huffman tables */ -static u16 *jpeg_huffman_tables(int *length) -{ - static u16 tables[] = { - 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, - 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, - 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, - 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, - 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, - 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, - 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, - 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, - 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, - 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, - 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, - 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, - 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, - 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, - 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, - 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, - 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, - 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, - 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, - 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, - 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, - 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, - 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, - 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, - 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, - 0xFF0B, - 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, - 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, - 0xFF0B - }; - - *length = ARRAY_SIZE(tables); - return tables; -} - -/****************************************************************************/ -/* MCHIP low-level functions */ -/****************************************************************************/ - -/* returns the horizontal capture size */ -static inline int mchip_hsize(void) -{ - return meye.params.subsample ? 320 : 640; -} - -/* returns the vertical capture size */ -static inline int mchip_vsize(void) -{ - return meye.params.subsample ? 240 : 480; -} - -/* waits for a register to be available */ -static void mchip_sync(int reg) -{ - u32 status; - int i; - - if (reg == MCHIP_MM_FIFO_DATA) { - for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { - status = readl(meye.mchip_mmregs + - MCHIP_MM_FIFO_STATUS); - if (!(status & MCHIP_MM_FIFO_WAIT)) { - printk(KERN_WARNING "meye: fifo not ready\n"); - return; - } - if (status & MCHIP_MM_FIFO_READY) - return; - udelay(1); - } - } else if (reg > 0x80) { - u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY - : MCHIP_HIC_STATUS_VRJ_RDY; - for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { - status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS); - if (status & mask) - return; - udelay(1); - } - } else - return; - printk(KERN_WARNING - "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", - reg, status); -} - -/* sets a value into the register */ -static inline void mchip_set(int reg, u32 v) -{ - mchip_sync(reg); - writel(v, meye.mchip_mmregs + reg); -} - -/* get the register value */ -static inline u32 mchip_read(int reg) -{ - mchip_sync(reg); - return readl(meye.mchip_mmregs + reg); -} - -/* wait for a register to become a particular value */ -static inline int mchip_delay(u32 reg, u32 v) -{ - int n = 10; - while (--n && mchip_read(reg) != v) - udelay(1); - return n; -} - -/* setup subsampling */ -static void mchip_subsample(void) -{ - mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample); - mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize()); - mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize()); - mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize()); - mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize()); - mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); -} - -/* set the framerate into the mchip */ -static void mchip_set_framerate(void) -{ - mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate); -} - -/* load some huffman and quantisation tables into the VRJ chip ready - for JPEG compression */ -static void mchip_load_tables(void) -{ - int i; - int length; - u16 *tables; - - tables = jpeg_huffman_tables(&length); - for (i = 0; i < length; i++) - writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); - - tables = jpeg_quantisation_tables(&length, meye.params.quality); - for (i = 0; i < length; i++) - writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); -} - -/* setup the VRJ parameters in the chip */ -static void mchip_vrj_setup(u8 mode) -{ - mchip_set(MCHIP_VRJ_BUS_MODE, 5); - mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f); - mchip_set(MCHIP_VRJ_PDAT_USE, 1); - mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0); - mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode); - mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize()); - mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize()); - mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b); - mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF); - mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF); - mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC); - mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0); - mchip_set(MCHIP_VRJ_SOF1, 0x601); - mchip_set(MCHIP_VRJ_SOF2, 0x1502); - mchip_set(MCHIP_VRJ_SOF3, 0x1503); - mchip_set(MCHIP_VRJ_SOF4, 0x1596); - mchip_set(MCHIP_VRJ_SOS, 0x0ed0); - - mchip_load_tables(); -} - -/* sets the DMA parameters into the chip */ -static void mchip_dma_setup(dma_addr_t dma_addr) -{ - int i; - - mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); - for (i = 0; i < 4; i++) - mchip_set(MCHIP_MM_FIR(i), 0); - meye.mchip_fnum = 0; -} - -/* setup for DMA transfers - also zeros the framebuffer */ -static int mchip_dma_alloc(void) -{ - if (!meye.mchip_dmahandle) - if (ptable_alloc()) - return -1; - return 0; -} - -/* frees the DMA buffer */ -static void mchip_dma_free(void) -{ - if (meye.mchip_dmahandle) { - mchip_dma_setup(0); - ptable_free(); - } -} - -/* stop any existing HIC action and wait for any dma to complete then - reset the dma engine */ -static void mchip_hic_stop(void) -{ - int i, j; - - meye.mchip_mode = MCHIP_HIC_MODE_NOOP; - if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) - return; - for (i = 0; i < 20; ++i) { - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP); - mchip_delay(MCHIP_HIC_CMD, 0); - for (j = 0; j < 100; ++j) { - if (mchip_delay(MCHIP_HIC_STATUS, - MCHIP_HIC_STATUS_IDLE)) - return; - msleep(1); - } - printk(KERN_ERR "meye: need to reset HIC!\n"); - - mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET); - msleep(250); - } - printk(KERN_ERR "meye: resetting HIC hanged!\n"); -} - -/****************************************************************************/ -/* MCHIP frame processing functions */ -/****************************************************************************/ - -/* get the next ready frame from the dma engine */ -static u32 mchip_get_frame(void) -{ - return mchip_read(MCHIP_MM_FIR(meye.mchip_fnum)); -} - -/* frees the current frame from the dma engine */ -static void mchip_free_frame(void) -{ - mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0); - meye.mchip_fnum++; - meye.mchip_fnum %= 4; -} - -/* read one frame from the framebuffer assuming it was captured using - a uncompressed transfer */ -static void mchip_cont_read_frame(u32 v, u8 *buf, int size) -{ - int pt_id; - - pt_id = (v >> 17) & 0x3FF; - - ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); -} - -/* read a compressed frame from the framebuffer */ -static int mchip_comp_read_frame(u32 v, u8 *buf, int size) -{ - int pt_start, pt_end, trailer; - int fsize; - int i; - - pt_start = (v >> 19) & 0xFF; - pt_end = (v >> 11) & 0xFF; - trailer = (v >> 1) & 0x3FF; - - if (pt_end < pt_start) - fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + - pt_end * PAGE_SIZE + trailer * 4; - else - fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; - - if (fsize > size) { - printk(KERN_WARNING "meye: oversized compressed frame %d\n", - fsize); - return -1; - } - - ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); - -#ifdef MEYE_JPEG_CORRECTION - - /* Some mchip generated jpeg frames are incorrect. In most - * (all ?) of those cases, the final EOI (0xff 0xd9) marker - * is not present at the end of the frame. - * - * Since adding the final marker is not enough to restore - * the jpeg integrity, we drop the frame. - */ - - for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ; - - if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9) - return -1; - -#endif - - return fsize; -} - -/* take a picture into SDRAM */ -static void mchip_take_picture(void) -{ - int i; - - mchip_hic_stop(); - mchip_subsample(); - mchip_dma_setup(meye.mchip_dmahandle); - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); - - for (i = 0; i < 100; ++i) { - if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) - break; - msleep(1); - } -} - -/* dma a previously taken picture into a buffer */ -static void mchip_get_picture(u8 *buf, int bufsize) -{ - u32 v; - int i; - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); - for (i = 0; i < 100; ++i) { - if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) - break; - msleep(1); - } - for (i = 0; i < 4; ++i) { - v = mchip_get_frame(); - if (v & MCHIP_MM_FIR_RDY) { - mchip_cont_read_frame(v, buf, bufsize); - break; - } - mchip_free_frame(); - } -} - -/* start continuous dma capture */ -static void mchip_continuous_start(void) -{ - mchip_hic_stop(); - mchip_subsample(); - mchip_set_framerate(); - mchip_dma_setup(meye.mchip_dmahandle); - - meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); -} - -/* compress one frame into a buffer */ -static int mchip_compress_frame(u8 *buf, int bufsize) -{ - u32 v; - int len = -1, i; - - mchip_vrj_setup(0x3f); - udelay(50); - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); - for (i = 0; i < 100; ++i) { - if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) - break; - msleep(1); - } - - for (i = 0; i < 4; ++i) { - v = mchip_get_frame(); - if (v & MCHIP_MM_FIR_RDY) { - len = mchip_comp_read_frame(v, buf, bufsize); - break; - } - mchip_free_frame(); - } - return len; -} - -#if 0 -/* uncompress one image into a buffer */ -static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) -{ - mchip_vrj_setup(0x3f); - udelay(50); - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); - - return mchip_comp_read_frame(buf, bufsize); -} -#endif - -/* start continuous compressed capture */ -static void mchip_cont_compression_start(void) -{ - mchip_hic_stop(); - mchip_vrj_setup(0x3f); - mchip_subsample(); - mchip_set_framerate(); - mchip_dma_setup(meye.mchip_dmahandle); - - meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; - - mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP); - mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); - - mchip_delay(MCHIP_HIC_CMD, 0); -} - -/****************************************************************************/ -/* Interrupt handling */ -/****************************************************************************/ - -static irqreturn_t meye_irq(int irq, void *dev_id) -{ - u32 v; - int reqnr; - static int sequence; - - v = mchip_read(MCHIP_MM_INTA); - - if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT && - meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) - return IRQ_NONE; - -again: - v = mchip_get_frame(); - if (!(v & MCHIP_MM_FIR_RDY)) - return IRQ_HANDLED; - - if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) { - if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, - sizeof(int), &meye.grabq_lock) != sizeof(int)) { - mchip_free_frame(); - return IRQ_HANDLED; - } - mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr, - mchip_hsize() * mchip_vsize() * 2); - meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; - meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - 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); - wake_up_interruptible(&meye.proc_list); - } else { - int size; - size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize); - if (size == -1) { - mchip_free_frame(); - goto again; - } - if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, - sizeof(int), &meye.grabq_lock) != sizeof(int)) { - mchip_free_frame(); - goto again; - } - memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp, - size); - meye.grab_buffer[reqnr].size = size; - meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - 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); - wake_up_interruptible(&meye.proc_list); - } - mchip_free_frame(); - goto again; -} - -/****************************************************************************/ -/* video4linux integration */ -/****************************************************************************/ - -static int meye_open(struct file *file) -{ - int i; - - if (test_and_set_bit(0, &meye.in_use)) - return -EBUSY; - - mchip_hic_stop(); - - if (mchip_dma_alloc()) { - printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); - clear_bit(0, &meye.in_use); - return -ENOBUFS; - } - - for (i = 0; i < MEYE_MAX_BUFNBRS; i++) - meye.grab_buffer[i].state = MEYE_BUF_UNUSED; - kfifo_reset(&meye.grabq); - kfifo_reset(&meye.doneq); - return v4l2_fh_open(file); -} - -static int meye_release(struct file *file) -{ - mchip_hic_stop(); - mchip_dma_free(); - clear_bit(0, &meye.in_use); - return v4l2_fh_release(file); -} - -static int meyeioc_g_params(struct meye_params *p) -{ - *p = meye.params; - return 0; -} - -static int meyeioc_s_params(struct meye_params *jp) -{ - if (jp->subsample > 1) - return -EINVAL; - - if (jp->quality > 10) - return -EINVAL; - - if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) - return -EINVAL; - - if (jp->framerate > 31) - return -EINVAL; - - mutex_lock(&meye.lock); - - if (meye.params.subsample != jp->subsample || - meye.params.quality != jp->quality) - mchip_hic_stop(); /* need restart */ - - meye.params = *jp; - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, - meye.params.sharpness); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, - meye.params.agc); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, - meye.params.picture); - mutex_unlock(&meye.lock); - - return 0; -} - -static int meyeioc_qbuf_capt(int *nb) -{ - if (!meye.grab_fbuffer) - return -EINVAL; - - if (*nb >= gbuffers) - return -EINVAL; - - if (*nb < 0) { - /* stop capture */ - mchip_hic_stop(); - return 0; - } - - if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) - return -EBUSY; - - mutex_lock(&meye.lock); - - if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) - mchip_cont_compression_start(); - - meye.grab_buffer[*nb].state = MEYE_BUF_USING; - kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int), - &meye.grabq_lock); - mutex_unlock(&meye.lock); - - return 0; -} - -static int meyeioc_sync(struct file *file, void *fh, int *i) -{ - int unused; - - if (*i < 0 || *i >= gbuffers) - return -EINVAL; - - mutex_lock(&meye.lock); - switch (meye.grab_buffer[*i].state) { - - case MEYE_BUF_UNUSED: - mutex_unlock(&meye.lock); - return -EINVAL; - case MEYE_BUF_USING: - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { - mutex_unlock(&meye.lock); - return -EINTR; - } - fallthrough; - case MEYE_BUF_DONE: - meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; - if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused, - sizeof(int), &meye.doneq_lock) != sizeof(int)) - break; - } - *i = meye.grab_buffer[*i].size; - mutex_unlock(&meye.lock); - return 0; -} - -static int meyeioc_stillcapt(void) -{ - if (!meye.grab_fbuffer) - return -EINVAL; - - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; - mchip_take_picture(); - - mchip_get_picture(meye.grab_fbuffer, - mchip_hsize() * mchip_vsize() * 2); - - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - - return 0; -} - -static int meyeioc_stilljcapt(int *len) -{ - if (!meye.grab_fbuffer) - return -EINVAL; - - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; - *len = -1; - - while (*len == -1) { - mchip_take_picture(); - *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); - } - - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - return 0; -} - -static int vidioc_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "meye", sizeof(cap->driver)); - strscpy(cap->card, "meye", sizeof(cap->card)); - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - - strscpy(i->name, "Camera", sizeof(i->name)); - i->type = V4L2_INPUT_TYPE_CAMERA; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int i) -{ - if (i != 0) - return -EINVAL; - - return 0; -} - -static int meye_s_ctrl(struct v4l2_ctrl *ctrl) -{ - mutex_lock(&meye.lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, ctrl->val); - meye.brightness = ctrl->val << 10; - break; - case V4L2_CID_HUE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAHUE, ctrl->val); - meye.hue = ctrl->val << 10; - break; - case V4L2_CID_CONTRAST: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACONTRAST, ctrl->val); - meye.contrast = ctrl->val << 10; - break; - case V4L2_CID_SATURATION: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACOLOR, ctrl->val); - meye.colour = ctrl->val << 10; - break; - case V4L2_CID_MEYE_AGC: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAAGC, ctrl->val); - meye.params.agc = ctrl->val; - break; - case V4L2_CID_SHARPNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERASHARPNESS, ctrl->val); - meye.params.sharpness = ctrl->val; - break; - case V4L2_CID_MEYE_PICTURE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAPICTURE, ctrl->val); - meye.params.picture = ctrl->val; - break; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - meye.params.quality = ctrl->val; - break; - case V4L2_CID_MEYE_FRAMERATE: - meye.params.framerate = ctrl->val; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (f->index > 1) - return -EINVAL; - - if (f->index == 0) { - /* standard YUV 422 capture */ - f->flags = 0; - f->pixelformat = V4L2_PIX_FMT_YUYV; - } else { - /* compressed MJPEG capture */ - f->pixelformat = V4L2_PIX_FMT_MJPEG; - } - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - } - - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - default: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - break; - case MCHIP_HIC_MODE_CONT_COMP: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - break; - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = mchip_hsize(); - f->fmt.pix.height = mchip_vsize(); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - - f->fmt.pix.field = V4L2_FIELD_NONE; - mutex_lock(&meye.lock); - - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - meye.params.subsample = 1; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - meye.params.subsample = 0; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; - break; - case V4L2_PIX_FMT_MJPEG: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; - break; - } - - mutex_unlock(&meye.lock); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *req) -{ - int i; - - if (req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (meye.grab_fbuffer && req->count == gbuffers) { - /* already allocated, no modifications */ - return 0; - } - - mutex_lock(&meye.lock); - if (meye.grab_fbuffer) { - for (i = 0; i < gbuffers; i++) - if (meye.vma_use_count[i]) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - rvfree(meye.grab_fbuffer, gbuffers * gbufsize); - meye.grab_fbuffer = NULL; - } - - gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); - req->count = gbuffers; - meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); - - if (!meye.grab_fbuffer) { - printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); - mutex_unlock(&meye.lock); - return -ENOMEM; - } - - for (i = 0; i < gbuffers; i++) - meye.vma_use_count[i] = 0; - - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - unsigned int index = buf->index; - - if (index >= gbuffers) - return -EINVAL; - - buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - if (meye.grab_buffer[index].state == MEYE_BUF_USING) - buf->flags |= V4L2_BUF_FLAG_QUEUED; - - if (meye.grab_buffer[index].state == MEYE_BUF_DONE) - buf->flags |= V4L2_BUF_FLAG_DONE; - - buf->field = V4L2_FIELD_NONE; - v4l2_buffer_set_timestamp(buf, meye.grab_buffer[index].ts); - buf->sequence = meye.grab_buffer[index].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * gbufsize; - buf->length = gbufsize; - - return 0; -} - -static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (buf->index >= gbuffers) - return -EINVAL; - - if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) - return -EINVAL; - - mutex_lock(&meye.lock); - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - meye.grab_buffer[buf->index].state = MEYE_BUF_USING; - kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index, - sizeof(int), &meye.grabq_lock); - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) -{ - int reqnr; - - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - mutex_lock(&meye.lock); - - if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - - if (wait_event_interruptible(meye.proc_list, - kfifo_len(&meye.doneq) != 0) < 0) { - mutex_unlock(&meye.lock); - return -EINTR; - } - - if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr, - sizeof(int), &meye.doneq_lock)) { - mutex_unlock(&meye.lock); - return -EBUSY; - } - - if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - - buf->index = reqnr; - buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - buf->field = V4L2_FIELD_NONE; - v4l2_buffer_set_timestamp(buf, meye.grab_buffer[reqnr].ts); - buf->sequence = meye.grab_buffer[reqnr].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = reqnr * gbufsize; - buf->length = gbufsize; - meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) -{ - mutex_lock(&meye.lock); - - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - mchip_continuous_start(); - break; - case MCHIP_HIC_MODE_CONT_COMP: - mchip_cont_compression_start(); - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) -{ - mutex_lock(&meye.lock); - mchip_hic_stop(); - kfifo_reset(&meye.grabq); - kfifo_reset(&meye.doneq); - - for (i = 0; i < MEYE_MAX_BUFNBRS; i++) - meye.grab_buffer[i].state = MEYE_BUF_UNUSED; - - mutex_unlock(&meye.lock); - return 0; -} - -static long vidioc_default(struct file *file, void *fh, bool valid_prio, - unsigned int cmd, void *arg) -{ - switch (cmd) { - case MEYEIOC_G_PARAMS: - return meyeioc_g_params((struct meye_params *) arg); - - case MEYEIOC_S_PARAMS: - return meyeioc_s_params((struct meye_params *) arg); - - case MEYEIOC_QBUF_CAPT: - return meyeioc_qbuf_capt((int *) arg); - - case MEYEIOC_SYNC: - return meyeioc_sync(file, fh, (int *) arg); - - case MEYEIOC_STILLCAPT: - return meyeioc_stillcapt(); - - case MEYEIOC_STILLJCAPT: - return meyeioc_stilljcapt((int *) arg); - - default: - return -ENOTTY; - } - -} - -static __poll_t meye_poll(struct file *file, poll_table *wait) -{ - __poll_t res = v4l2_ctrl_poll(file, wait); - - mutex_lock(&meye.lock); - poll_wait(file, &meye.proc_list, wait); - if (kfifo_len(&meye.doneq)) - res |= EPOLLIN | EPOLLRDNORM; - mutex_unlock(&meye.lock); - return res; -} - -static void meye_vm_open(struct vm_area_struct *vma) -{ - long idx = (long)vma->vm_private_data; - meye.vma_use_count[idx]++; -} - -static void meye_vm_close(struct vm_area_struct *vma) -{ - long idx = (long)vma->vm_private_data; - meye.vma_use_count[idx]--; -} - -static const struct vm_operations_struct meye_vm_ops = { - .open = meye_vm_open, - .close = meye_vm_close, -}; - -static int meye_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long page, pos; - - mutex_lock(&meye.lock); - if (size > gbuffers * gbufsize || offset > gbuffers * gbufsize - size) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - if (!meye.grab_fbuffer) { - int i; - - /* lazy allocation */ - meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize); - if (!meye.grab_fbuffer) { - printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); - mutex_unlock(&meye.lock); - return -ENOMEM; - } - for (i = 0; i < gbuffers; i++) - meye.vma_use_count[i] = 0; - } - pos = (unsigned long)meye.grab_fbuffer + offset; - - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - vma->vm_ops = &meye_vm_ops; - vma->vm_flags &= ~VM_IO; /* not I/O memory */ - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_private_data = (void *) (offset / gbufsize); - meye_vm_open(vma); - - mutex_unlock(&meye.lock); - return 0; -} - -static const struct v4l2_file_operations meye_fops = { - .owner = THIS_MODULE, - .open = meye_open, - .release = meye_release, - .mmap = meye_mmap, - .unlocked_ioctl = video_ioctl2, - .poll = meye_poll, -}; - -static const struct v4l2_ioctl_ops meye_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_default = vidioc_default, -}; - -static const struct video_device meye_template = { - .name = "meye", - .fops = &meye_fops, - .ioctl_ops = &meye_ioctl_ops, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, -}; - -static const struct v4l2_ctrl_ops meye_ctrl_ops = { - .s_ctrl = meye_s_ctrl, -}; - -static int __maybe_unused meye_suspend(struct device *dev) -{ - meye.pm_mchip_mode = meye.mchip_mode; - mchip_hic_stop(); - mchip_set(MCHIP_MM_INTA, 0x0); - return 0; -} - -static int __maybe_unused meye_resume(struct device *dev) -{ - pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); - - mchip_delay(MCHIP_HIC_CMD, 0); - mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); - msleep(1); - mchip_set(MCHIP_VRJ_SOFT_RESET, 1); - msleep(1); - mchip_set(MCHIP_MM_PCI_MODE, 5); - msleep(1); - mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); - - switch (meye.pm_mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - mchip_continuous_start(); - break; - case MCHIP_HIC_MODE_CONT_COMP: - mchip_cont_compression_start(); - break; - } - return 0; -} - -static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) -{ - static const struct v4l2_ctrl_config ctrl_agc = { - .id = V4L2_CID_MEYE_AGC, - .type = V4L2_CTRL_TYPE_INTEGER, - .ops = &meye_ctrl_ops, - .name = "AGC", - .max = 63, - .step = 1, - .def = 48, - .flags = V4L2_CTRL_FLAG_SLIDER, - }; - static const struct v4l2_ctrl_config ctrl_picture = { - .id = V4L2_CID_MEYE_PICTURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .ops = &meye_ctrl_ops, - .name = "Picture", - .max = 63, - .step = 1, - }; - static const struct v4l2_ctrl_config ctrl_framerate = { - .id = V4L2_CID_MEYE_FRAMERATE, - .type = V4L2_CTRL_TYPE_INTEGER, - .ops = &meye_ctrl_ops, - .name = "Framerate", - .max = 31, - .step = 1, - }; - struct v4l2_device *v4l2_dev = &meye.v4l2_dev; - int ret = -EBUSY; - unsigned long mchip_adr; - - if (meye.mchip_dev != NULL) { - printk(KERN_ERR "meye: only one device allowed!\n"); - return ret; - } - - ret = v4l2_device_register(&pcidev->dev, v4l2_dev); - if (ret < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return ret; - } - ret = -ENOMEM; - meye.mchip_dev = pcidev; - - meye.grab_temp = vmalloc(array_size(PAGE_SIZE, MCHIP_NB_PAGES_MJPEG)); - if (!meye.grab_temp) - goto outvmalloc; - - spin_lock_init(&meye.grabq_lock); - if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, - GFP_KERNEL)) - goto outkfifoalloc1; - - spin_lock_init(&meye.doneq_lock); - if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, - GFP_KERNEL)) - goto outkfifoalloc2; - - meye.vdev = meye_template; - meye.vdev.v4l2_dev = &meye.v4l2_dev; - - ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1); - if (ret) { - v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); - v4l2_err(v4l2_dev, "meye: did you enable the camera in sonypi using the module options ?\n"); - goto outsonypienable; - } - - ret = pci_enable_device(meye.mchip_dev); - if (ret) { - v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); - goto outenabledev; - } - - ret = -EIO; - mchip_adr = pci_resource_start(meye.mchip_dev,0); - if (!mchip_adr) { - v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); - goto outregions; - } - if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), - pci_resource_len(meye.mchip_dev, 0), - "meye")) { - v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); - goto outregions; - } - meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); - if (!meye.mchip_mmregs) { - v4l2_err(v4l2_dev, "meye: ioremap failed\n"); - goto outremap; - } - - meye.mchip_irq = pcidev->irq; - if (request_irq(meye.mchip_irq, meye_irq, - IRQF_SHARED, "meye", meye_irq)) { - v4l2_err(v4l2_dev, "request_irq failed\n"); - goto outreqirq; - } - - pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8); - pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64); - - pci_set_master(meye.mchip_dev); - - /* Ask the camera to perform a soft reset. */ - pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); - - mchip_delay(MCHIP_HIC_CMD, 0); - mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); - - msleep(1); - mchip_set(MCHIP_VRJ_SOFT_RESET, 1); - - msleep(1); - mchip_set(MCHIP_MM_PCI_MODE, 5); - - msleep(1); - mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); - - mutex_init(&meye.lock); - init_waitqueue_head(&meye.proc_list); - - v4l2_ctrl_handler_init(&meye.hdl, 3); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_HUE, 0, 63, 1, 32); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_CONTRAST, 0, 63, 1, 32); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_SATURATION, 0, 63, 1, 32); - v4l2_ctrl_new_custom(&meye.hdl, &ctrl_agc, NULL); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 63, 1, 32); - v4l2_ctrl_new_custom(&meye.hdl, &ctrl_picture, NULL); - v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, 10, 1, 8); - v4l2_ctrl_new_custom(&meye.hdl, &ctrl_framerate, NULL); - if (meye.hdl.error) { - v4l2_err(v4l2_dev, "couldn't register controls\n"); - goto outvideoreg; - } - - v4l2_ctrl_handler_setup(&meye.hdl); - meye.vdev.ctrl_handler = &meye.hdl; - - if (video_register_device(&meye.vdev, VFL_TYPE_VIDEO, - video_nr) < 0) { - v4l2_err(v4l2_dev, "video_register_device failed\n"); - goto outvideoreg; - } - - v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", - MEYE_DRIVER_VERSION); - v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", - meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); - - return 0; - -outvideoreg: - v4l2_ctrl_handler_free(&meye.hdl); - free_irq(meye.mchip_irq, meye_irq); -outreqirq: - iounmap(meye.mchip_mmregs); -outremap: - release_mem_region(pci_resource_start(meye.mchip_dev, 0), - pci_resource_len(meye.mchip_dev, 0)); -outregions: - pci_disable_device(meye.mchip_dev); -outenabledev: - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); -outsonypienable: - kfifo_free(&meye.doneq); -outkfifoalloc2: - kfifo_free(&meye.grabq); -outkfifoalloc1: - vfree(meye.grab_temp); -outvmalloc: - return ret; -} - -static void meye_remove(struct pci_dev *pcidev) -{ - video_unregister_device(&meye.vdev); - - mchip_hic_stop(); - - mchip_dma_free(); - - /* disable interrupts */ - mchip_set(MCHIP_MM_INTA, 0x0); - - free_irq(meye.mchip_irq, meye_irq); - - iounmap(meye.mchip_mmregs); - - release_mem_region(pci_resource_start(meye.mchip_dev, 0), - pci_resource_len(meye.mchip_dev, 0)); - - pci_disable_device(meye.mchip_dev); - - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); - - kfifo_free(&meye.doneq); - kfifo_free(&meye.grabq); - - vfree(meye.grab_temp); - - if (meye.grab_fbuffer) { - rvfree(meye.grab_fbuffer, gbuffers*gbufsize); - meye.grab_fbuffer = NULL; - } - - printk(KERN_INFO "meye: removed\n"); -} - -static const struct pci_device_id meye_pci_tbl[] = { - { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, - { } -}; - -MODULE_DEVICE_TABLE(pci, meye_pci_tbl); - -static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); - -static struct pci_driver meye_driver = { - .name = "meye", - .id_table = meye_pci_tbl, - .probe = meye_probe, - .remove = meye_remove, - .driver.pm = &meye_pm_ops, -}; - -static int __init meye_init(void) -{ - gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS)); - if (gbufsize > MEYE_MAX_BUFSIZE) - gbufsize = MEYE_MAX_BUFSIZE; - gbufsize = PAGE_ALIGN(gbufsize); - printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) for capture\n", - gbuffers, - gbufsize / 1024, gbuffers * gbufsize / 1024); - return pci_register_driver(&meye_driver); -} - -static void __exit meye_exit(void) -{ - pci_unregister_driver(&meye_driver); -} - -module_init(meye_init); -module_exit(meye_exit); diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h deleted file mode 100644 index 5fa6552cf93d..000000000000 --- a/drivers/media/pci/meye/meye.h +++ /dev/null @@ -1,311 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Motion Eye video4linux driver for Sony Vaio PictureBook - * - * Copyright (C) 2001-2004 Stelian Pop - * - * Copyright (C) 2001-2002 Alcôve - * - * Copyright (C) 2000 Andrew Tridgell - * - * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. - * - * Some parts borrowed from various video4linux drivers, especially - * bttv-driver.c and zoran.c, see original files for credits. - */ - -#ifndef _MEYE_PRIV_H_ -#define _MEYE_PRIV_H_ - -#define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 14 - -#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ - __stringify(MEYE_DRIVER_MINORVERSION) - -#include -#include -#include -#include - -/****************************************************************************/ -/* Motion JPEG chip registers */ -/****************************************************************************/ - -/* Motion JPEG chip PCI configuration registers */ -#define MCHIP_PCI_POWER_CSR 0x54 -#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */ -#define MCHIP_PCI_HOSTUSEREQ_SET 0x64 -#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68 -#define MCHIP_PCI_LOWPOWER_SET 0x6c -#define MCHIP_PCI_LOWPOWER_CLR 0x70 -#define MCHIP_PCI_SOFTRESET_SET 0x74 - -/* Motion JPEG chip memory mapped registers */ -#define MCHIP_MM_REGS 0x200 /* 512 bytes */ -#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */ -#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */ - -#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */ -#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */ -#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */ -#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */ - -#define MCHIP_MM_INTA 0x04 /* Int status/mask */ -#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */ -#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */ -#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */ -#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */ -#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */ -#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800 -#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */ -#define MCHIP_MM_INTA_JPEG_MASK 0x00001000 -#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */ -#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */ -#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000 - -#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/ - /* n*4kB */ -#define MCHIP_NB_PAGES 1024 /* pages for display */ -#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */ - -#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */ -#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */ -#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */ -#define MCHIP_MM_FIR_FAILFR_SHIFT 27 - - /* continuous comp/decomp mode */ -#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */ -#define MCHIP_MM_FIR_C_ENDL_SHIFT 1 -#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */ -#define MCHIP_MM_FIR_C_ENDP_SHIFT 11 -#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */ -#define MCHIP_MM_FIR_C_STARTP_SHIFT 19 - - /* continuous picture output mode */ -#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */ -#define MCHIP_MM_FIR_O_STARTP_SHIFT 17 - -#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */ -#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */ -#define MCHIP_MM_FIFO_MASK 0x00000003 -#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/ -#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */ -#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */ -#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */ -#define MCHIP_MM_FIFO_READY 0x3 /* data ready */ - -#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */ - -#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */ - -#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */ - -#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */ - -#define MCHIP_HIC_CTL 0x50 /* HIC control */ -#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */ -#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */ - -#define MCHIP_HIC_CMD 0x54 /* HIC command */ -#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/ -#define MCHIP_HIC_CMD_NOOP 0x0 -#define MCHIP_HIC_CMD_START 0x1 -#define MCHIP_HIC_CMD_STOP 0x2 - -#define MCHIP_HIC_MODE 0x58 -#define MCHIP_HIC_MODE_NOOP 0x0 -#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */ -#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */ -#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */ -#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */ -#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */ -#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */ -#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */ -#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */ - -#define MCHIP_HIC_STATUS 0x5c -#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */ -#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */ -#define MCHIP_HIC_STATUS_IDLE 0x00000003 -#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */ -#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */ -#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */ - -#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */ - -#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */ -#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */ - /* 1: Y' V Y U */ - -#define MCHIP_MCC_CMD 0x80 /* MCC commands */ -#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */ -#define MCHIP_MCC_CMD_IIC_START_SET 0x1 -#define MCHIP_MCC_CMD_IIC_END_SET 0x2 -#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */ -#define MCHIP_MCC_CMD_FM_READ 0x4 -#define MCHIP_MCC_CMD_FM_STOP 0x5 -#define MCHIP_MCC_CMD_CAPTURE 0x6 -#define MCHIP_MCC_CMD_DISPLAY 0x7 -#define MCHIP_MCC_CMD_END_DISP 0x8 -#define MCHIP_MCC_CMD_STILL_COMP 0x9 -#define MCHIP_MCC_CMD_STILL_DECOMP 0xa -#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb -#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc -#define MCHIP_MCC_CMD_CONT_COMP 0xd -#define MCHIP_MCC_CMD_CONT_DECOMP 0xe -#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */ - -#define MCHIP_MCC_IIC_WR 0x84 - -#define MCHIP_MCC_MCC_WR 0x88 - -#define MCHIP_MCC_MCC_RD 0x8c - -#define MCHIP_MCC_STATUS 0x90 -#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */ -#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */ -#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */ -#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */ -#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */ -#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */ -#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */ -#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */ - -#define MCHIP_MCC_SIG_POLARITY 0x94 -#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */ -#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */ -#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */ - -#define MCHIP_MCC_IRQ 0x98 -#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */ -#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010 -#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */ -#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020 -#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */ -#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040 -#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */ -#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080 - -#define MCHIP_MCC_HSTART 0x9c /* video in */ -#define MCHIP_MCC_VSTART 0xa0 -#define MCHIP_MCC_HCOUNT 0xa4 -#define MCHIP_MCC_VCOUNT 0xa8 -#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */ -#define MCHIP_MCC_R_YBASE 0xb0 -#define MCHIP_MCC_R_XRANGE 0xb4 -#define MCHIP_MCC_R_YRANGE 0xb8 -#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */ -#define MCHIP_MCC_B_YBASE 0xc0 -#define MCHIP_MCC_B_XRANGE 0xc4 -#define MCHIP_MCC_B_YRANGE 0xc8 - -#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */ - -#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */ - -/* VRJ registers (see table 12.2.4) */ -#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0 -#define MCHIP_VRJ_PIXEL_DATA 0x1b8 - -#define MCHIP_VRJ_BUS_MODE 0x100 -#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108 -#define MCHIP_VRJ_PDAT_USE 0x110 -#define MCHIP_VRJ_MODE_SPECIFY 0x118 -#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120 -#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124 -#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128 -#define MCHIP_VRJ_TABLE_DATA 0x140 -#define MCHIP_VRJ_RESTART_INTERVAL 0x148 -#define MCHIP_VRJ_NUM_LINES 0x150 -#define MCHIP_VRJ_NUM_PIXELS 0x158 -#define MCHIP_VRJ_NUM_COMPONENTS 0x160 -#define MCHIP_VRJ_SOF1 0x168 -#define MCHIP_VRJ_SOF2 0x170 -#define MCHIP_VRJ_SOF3 0x178 -#define MCHIP_VRJ_SOF4 0x180 -#define MCHIP_VRJ_SOS 0x188 -#define MCHIP_VRJ_SOFT_RESET 0x190 - -#define MCHIP_VRJ_STATUS 0x1c0 -#define MCHIP_VRJ_STATUS_BUSY 0x00001 -#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002 -#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004 -#define MCHIP_VRJ_STATUS_ERROR 0x00008 - -#define MCHIP_VRJ_IRQ_FLAG 0x1c8 -#define MCHIP_VRJ_ERROR_REPORT 0x1d8 - -#define MCHIP_VRJ_START_COMMAND 0x1a0 - -/****************************************************************************/ -/* Driver definitions. */ -/****************************************************************************/ - -/* Sony Programmable I/O Controller for accessing the camera commands */ -#include - -/* private API definitions */ -#include -#include - - -/* Enable jpg software correction */ -#define MEYE_JPEG_CORRECTION 1 - -/* Maximum size of a buffer */ -#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */ - -/* Maximum number of buffers */ -#define MEYE_MAX_BUFNBRS 32 - -/* State of a buffer */ -#define MEYE_BUF_UNUSED 0 /* not used */ -#define MEYE_BUF_USING 1 /* currently grabbing / playing */ -#define MEYE_BUF_DONE 2 /* done */ - -/* grab buffer */ -struct meye_grab_buffer { - int state; /* state of buffer */ - unsigned long size; /* size of jpg frame */ - u64 ts; /* timestamp */ - unsigned long sequence; /* sequence number */ -}; - -/* size of kfifos containing buffer indices */ -#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS - -/* Motion Eye device structure */ -struct meye { - struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ - struct v4l2_ctrl_handler hdl; - struct pci_dev *mchip_dev; /* pci device */ - u8 mchip_irq; /* irq */ - u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ - u8 mchip_fnum; /* current mchip frame number */ - unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ - u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ - void *mchip_ptable_toc; /* mchip: ptable toc */ - dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ - unsigned char *grab_fbuffer; /* capture framebuffer */ - unsigned char *grab_temp; /* temporary buffer */ - /* list of buffers */ - struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; - int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ - struct mutex lock; /* mutex for open/mmap... */ - struct kfifo grabq; /* queue for buffers to be grabbed */ - spinlock_t grabq_lock; /* lock protecting the queue */ - struct kfifo doneq; /* queue for grabbed buffers */ - spinlock_t doneq_lock; /* lock protecting the queue */ - wait_queue_head_t proc_list; /* wait queue */ - struct video_device vdev; /* video device parameters */ - u16 brightness; - u16 hue; - u16 contrast; - u16 colour; - struct meye_params params; /* additional parameters */ - unsigned long in_use; /* set to 1 if the device is in use */ - u8 pm_mchip_mode; /* old mchip mode */ -}; - -#endif diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 9781080c6e7d..ed3e484603d7 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -56,6 +56,7 @@ menuconfig STAGING_MEDIA_DEPRECATED if STAGING_MEDIA_DEPRECATED source "drivers/staging/media/deprecated/cpia2/Kconfig" +source "drivers/staging/media/deprecated/meye/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index adcf128d27b4..822c70ab4c0b 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_CPIA2) += deprecated/cpia2/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ +obj-$(CONFIG_VIDEO_MEYE) += deprecated/meye/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ obj-$(CONFIG_VIDEO_STKWEBCAM) += deprecated/stkwebcam/ diff --git a/drivers/staging/media/deprecated/meye/Kconfig b/drivers/staging/media/deprecated/meye/Kconfig new file mode 100644 index 000000000000..f135f8568c85 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEYE + tristate "Sony Vaio Picturebook Motion Eye Video For Linux (DEPRECATED)" + depends on PCI && VIDEO_DEV + depends on SONY_LAPTOP + depends on X86 || COMPILE_TEST + help + This is the video4linux driver for the Motion Eye camera found + in the Vaio Picturebook laptops. Please read the material in + for more information. + + If you say Y or M here, you need to say Y or M to "Sony Laptop + Extras" in the misc device section. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called meye. diff --git a/drivers/staging/media/deprecated/meye/Makefile b/drivers/staging/media/deprecated/meye/Makefile new file mode 100644 index 000000000000..36f1f86f0d58 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_MEYE) += meye.o diff --git a/drivers/staging/media/deprecated/meye/TODO b/drivers/staging/media/deprecated/meye/TODO new file mode 100644 index 000000000000..6d1d1433d5a0 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/TODO @@ -0,0 +1,6 @@ +The meye driver does not use the vb2 framework for streaming +video, instead it implements this in the driver. + +To prevent removal of this driver early 2023 it has to be +converted to use vb2. Contact the linux-media@vger.kernel.org +mailing list if you want to do this. diff --git a/drivers/staging/media/deprecated/meye/meye.c b/drivers/staging/media/deprecated/meye/meye.c new file mode 100644 index 000000000000..5d87efd9b95c --- /dev/null +++ b/drivers/staging/media/deprecated/meye/meye.c @@ -0,0 +1,1814 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve + * + * Copyright (C) 2000 Andrew Tridgell + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "meye.h" +#include + +MODULE_AUTHOR("Stelian Pop "); +MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEYE_DRIVER_VERSION); + +/* number of grab buffers */ +static unsigned int gbuffers = 2; +module_param(gbuffers, int, 0444); +MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)"); + +/* size of a grab buffer */ +static unsigned int gbufsize = MEYE_MAX_BUFSIZE; +module_param(gbufsize, int, 0444); +MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400 (will be rounded up to a page multiple)"); + +/* /dev/videoX registration number */ +static int video_nr = -1; +module_param(video_nr, int, 0444); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +/* driver structure - only one possible */ +static struct meye meye; + +/****************************************************************************/ +/* Memory allocation routines (stolen from bttv-driver.c) */ +/****************************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (mem) { + memset(mem, 0, size); + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) { + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); + } +} + +/* + * return a page table pointing to N pages of locked memory + * + * NOTE: The meye device expects DMA addresses on 32 bits, we build + * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. + */ +static int ptable_alloc(void) +{ + u32 *pt; + int i; + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + + /* give only 32 bit DMA addresses */ + if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32))) + return -1; + + meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &meye.mchip_dmahandle, + GFP_KERNEL); + if (!meye.mchip_ptable_toc) { + meye.mchip_dmahandle = 0; + return -1; + } + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma; + meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &dma, + GFP_KERNEL); + if (!meye.mchip_ptable[i]) { + int j; + pt = meye.mchip_ptable_toc; + for (j = 0; j < i; ++j) { + dma = (dma_addr_t) *pt; + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[j], dma); + pt++; + } + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; + return -1; + } + *pt = (u32) dma; + pt++; + } + return 0; +} + +static void ptable_free(void) +{ + u32 *pt; + int i; + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma = (dma_addr_t) *pt; + if (meye.mchip_ptable[i]) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[i], dma); + pt++; + } + + if (meye.mchip_ptable_toc) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; +} + +/* copy data from ptable into buf */ +static void ptable_copy(u8 *buf, int start, int size, int pt_pages) +{ + int i; + + for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { + memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); + if (start >= pt_pages) + start = 0; + } + memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); +} + +/****************************************************************************/ +/* JPEG tables at different qualities to load into the VRJ chip */ +/****************************************************************************/ + +/* return a set of quantisation tables based on a quality from 1 to 10 */ +static u16 *jpeg_quantisation_tables(int *length, int quality) +{ + static u16 jpeg_tables[][70] = { { + 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, + 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, + 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, + 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, + 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, + 0xe6ff, 0xfffd, 0xfff8, + 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, + 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xfff8, + }, + { + 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, + 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, + 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, + 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, + 0x99c7, 0xaba8, 0xffa4, + 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, + 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xffa4, + }, + { + 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, + 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, + 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, + 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, + 0x7396, 0x817e, 0xff7c, + 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, + 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0xff7c, + }, + { + 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, + 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, + 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, + 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, + 0x5c78, 0x6765, 0xff63, + 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, + 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0xff63, + }, + { + 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, + 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, + 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, + 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, + 0x4a60, 0x5251, 0xff4f, + 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, + 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0xff4f, + }, + { + 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, + 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, + 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, + 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, + 0x3748, 0x3e3d, 0xff3b, + 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, + 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0xff3b, + }, + { + 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, + 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, + 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, + 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, + 0x2530, 0x2928, 0xff28, + 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, + 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0xff28, + }, + { + 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, + 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, + 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, + 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, + 0x1218, 0x1514, 0xff14, + 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, + 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0xff14, + }, + { + 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + } }; + + if (quality < 0 || quality > 10) { + printk(KERN_WARNING + "meye: invalid quality level %d - using 8\n", quality); + quality = 8; + } + + *length = ARRAY_SIZE(jpeg_tables[quality]); + return jpeg_tables[quality]; +} + +/* return a generic set of huffman tables */ +static u16 *jpeg_huffman_tables(int *length) +{ + static u16 tables[] = { + 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, + 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, + 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, + 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, + 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, + 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, + 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, + 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, + 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, + 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, + 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, + 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, + 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, + 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, + 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, + 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, + 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, + 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, + 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, + 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, + 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B, + 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B + }; + + *length = ARRAY_SIZE(tables); + return tables; +} + +/****************************************************************************/ +/* MCHIP low-level functions */ +/****************************************************************************/ + +/* returns the horizontal capture size */ +static inline int mchip_hsize(void) +{ + return meye.params.subsample ? 320 : 640; +} + +/* returns the vertical capture size */ +static inline int mchip_vsize(void) +{ + return meye.params.subsample ? 240 : 480; +} + +/* waits for a register to be available */ +static void mchip_sync(int reg) +{ + u32 status; + int i; + + if (reg == MCHIP_MM_FIFO_DATA) { + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + + MCHIP_MM_FIFO_STATUS); + if (!(status & MCHIP_MM_FIFO_WAIT)) { + printk(KERN_WARNING "meye: fifo not ready\n"); + return; + } + if (status & MCHIP_MM_FIFO_READY) + return; + udelay(1); + } + } else if (reg > 0x80) { + u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY + : MCHIP_HIC_STATUS_VRJ_RDY; + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS); + if (status & mask) + return; + udelay(1); + } + } else + return; + printk(KERN_WARNING + "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", + reg, status); +} + +/* sets a value into the register */ +static inline void mchip_set(int reg, u32 v) +{ + mchip_sync(reg); + writel(v, meye.mchip_mmregs + reg); +} + +/* get the register value */ +static inline u32 mchip_read(int reg) +{ + mchip_sync(reg); + return readl(meye.mchip_mmregs + reg); +} + +/* wait for a register to become a particular value */ +static inline int mchip_delay(u32 reg, u32 v) +{ + int n = 10; + while (--n && mchip_read(reg) != v) + udelay(1); + return n; +} + +/* setup subsampling */ +static void mchip_subsample(void) +{ + mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample); + mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize()); + mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize()); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); +} + +/* set the framerate into the mchip */ +static void mchip_set_framerate(void) +{ + mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate); +} + +/* load some huffman and quantisation tables into the VRJ chip ready + for JPEG compression */ +static void mchip_load_tables(void) +{ + int i; + int length; + u16 *tables; + + tables = jpeg_huffman_tables(&length); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); + + tables = jpeg_quantisation_tables(&length, meye.params.quality); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); +} + +/* setup the VRJ parameters in the chip */ +static void mchip_vrj_setup(u8 mode) +{ + mchip_set(MCHIP_VRJ_BUS_MODE, 5); + mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f); + mchip_set(MCHIP_VRJ_PDAT_USE, 1); + mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0); + mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode); + mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize()); + mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize()); + mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF); + mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC); + mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0); + mchip_set(MCHIP_VRJ_SOF1, 0x601); + mchip_set(MCHIP_VRJ_SOF2, 0x1502); + mchip_set(MCHIP_VRJ_SOF3, 0x1503); + mchip_set(MCHIP_VRJ_SOF4, 0x1596); + mchip_set(MCHIP_VRJ_SOS, 0x0ed0); + + mchip_load_tables(); +} + +/* sets the DMA parameters into the chip */ +static void mchip_dma_setup(dma_addr_t dma_addr) +{ + int i; + + mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); + for (i = 0; i < 4; i++) + mchip_set(MCHIP_MM_FIR(i), 0); + meye.mchip_fnum = 0; +} + +/* setup for DMA transfers - also zeros the framebuffer */ +static int mchip_dma_alloc(void) +{ + if (!meye.mchip_dmahandle) + if (ptable_alloc()) + return -1; + return 0; +} + +/* frees the DMA buffer */ +static void mchip_dma_free(void) +{ + if (meye.mchip_dmahandle) { + mchip_dma_setup(0); + ptable_free(); + } +} + +/* stop any existing HIC action and wait for any dma to complete then + reset the dma engine */ +static void mchip_hic_stop(void) +{ + int i, j; + + meye.mchip_mode = MCHIP_HIC_MODE_NOOP; + if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) + return; + for (i = 0; i < 20; ++i) { + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP); + mchip_delay(MCHIP_HIC_CMD, 0); + for (j = 0; j < 100; ++j) { + if (mchip_delay(MCHIP_HIC_STATUS, + MCHIP_HIC_STATUS_IDLE)) + return; + msleep(1); + } + printk(KERN_ERR "meye: need to reset HIC!\n"); + + mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET); + msleep(250); + } + printk(KERN_ERR "meye: resetting HIC hanged!\n"); +} + +/****************************************************************************/ +/* MCHIP frame processing functions */ +/****************************************************************************/ + +/* get the next ready frame from the dma engine */ +static u32 mchip_get_frame(void) +{ + return mchip_read(MCHIP_MM_FIR(meye.mchip_fnum)); +} + +/* frees the current frame from the dma engine */ +static void mchip_free_frame(void) +{ + mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0); + meye.mchip_fnum++; + meye.mchip_fnum %= 4; +} + +/* read one frame from the framebuffer assuming it was captured using + a uncompressed transfer */ +static void mchip_cont_read_frame(u32 v, u8 *buf, int size) +{ + int pt_id; + + pt_id = (v >> 17) & 0x3FF; + + ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); +} + +/* read a compressed frame from the framebuffer */ +static int mchip_comp_read_frame(u32 v, u8 *buf, int size) +{ + int pt_start, pt_end, trailer; + int fsize; + int i; + + pt_start = (v >> 19) & 0xFF; + pt_end = (v >> 11) & 0xFF; + trailer = (v >> 1) & 0x3FF; + + if (pt_end < pt_start) + fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + + pt_end * PAGE_SIZE + trailer * 4; + else + fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; + + if (fsize > size) { + printk(KERN_WARNING "meye: oversized compressed frame %d\n", + fsize); + return -1; + } + + ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); + +#ifdef MEYE_JPEG_CORRECTION + + /* Some mchip generated jpeg frames are incorrect. In most + * (all ?) of those cases, the final EOI (0xff 0xd9) marker + * is not present at the end of the frame. + * + * Since adding the final marker is not enough to restore + * the jpeg integrity, we drop the frame. + */ + + for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ; + + if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9) + return -1; + +#endif + + return fsize; +} + +/* take a picture into SDRAM */ +static void mchip_take_picture(void) +{ + int i; + + mchip_hic_stop(); + mchip_subsample(); + mchip_dma_setup(meye.mchip_dmahandle); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } +} + +/* dma a previously taken picture into a buffer */ +static void mchip_get_picture(u8 *buf, int bufsize) +{ + u32 v; + int i; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + mchip_cont_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } +} + +/* start continuous dma capture */ +static void mchip_continuous_start(void) +{ + mchip_hic_stop(); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/* compress one frame into a buffer */ +static int mchip_compress_frame(u8 *buf, int bufsize) +{ + u32 v; + int len = -1, i; + + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + len = mchip_comp_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } + return len; +} + +#if 0 +/* uncompress one image into a buffer */ +static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) +{ + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + return mchip_comp_read_frame(buf, bufsize); +} +#endif + +/* start continuous compressed capture */ +static void mchip_cont_compression_start(void) +{ + mchip_hic_stop(); + mchip_vrj_setup(0x3f); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/****************************************************************************/ +/* Interrupt handling */ +/****************************************************************************/ + +static irqreturn_t meye_irq(int irq, void *dev_id) +{ + u32 v; + int reqnr; + static int sequence; + + v = mchip_read(MCHIP_MM_INTA); + + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT && + meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + return IRQ_NONE; + +again: + v = mchip_get_frame(); + if (!(v & MCHIP_MM_FIR_RDY)) + return IRQ_HANDLED; + + if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) { + if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, + sizeof(int), &meye.grabq_lock) != sizeof(int)) { + mchip_free_frame(); + return IRQ_HANDLED; + } + mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr, + mchip_hsize() * mchip_vsize() * 2); + meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + 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); + wake_up_interruptible(&meye.proc_list); + } else { + int size; + size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize); + if (size == -1) { + mchip_free_frame(); + goto again; + } + if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, + sizeof(int), &meye.grabq_lock) != sizeof(int)) { + mchip_free_frame(); + goto again; + } + memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp, + size); + meye.grab_buffer[reqnr].size = size; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + 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); + wake_up_interruptible(&meye.proc_list); + } + mchip_free_frame(); + goto again; +} + +/****************************************************************************/ +/* video4linux integration */ +/****************************************************************************/ + +static int meye_open(struct file *file) +{ + int i; + + if (test_and_set_bit(0, &meye.in_use)) + return -EBUSY; + + mchip_hic_stop(); + + if (mchip_dma_alloc()) { + printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); + clear_bit(0, &meye.in_use); + return -ENOBUFS; + } + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + kfifo_reset(&meye.grabq); + kfifo_reset(&meye.doneq); + return v4l2_fh_open(file); +} + +static int meye_release(struct file *file) +{ + mchip_hic_stop(); + mchip_dma_free(); + clear_bit(0, &meye.in_use); + return v4l2_fh_release(file); +} + +static int meyeioc_g_params(struct meye_params *p) +{ + *p = meye.params; + return 0; +} + +static int meyeioc_s_params(struct meye_params *jp) +{ + if (jp->subsample > 1) + return -EINVAL; + + if (jp->quality > 10) + return -EINVAL; + + if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) + return -EINVAL; + + if (jp->framerate > 31) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (meye.params.subsample != jp->subsample || + meye.params.quality != jp->quality) + mchip_hic_stop(); /* need restart */ + + meye.params = *jp; + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, + meye.params.sharpness); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, + meye.params.agc); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, + meye.params.picture); + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_qbuf_capt(int *nb) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (*nb >= gbuffers) + return -EINVAL; + + if (*nb < 0) { + /* stop capture */ + mchip_hic_stop(); + return 0; + } + + if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + mchip_cont_compression_start(); + + meye.grab_buffer[*nb].state = MEYE_BUF_USING; + kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int), + &meye.grabq_lock); + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_sync(struct file *file, void *fh, int *i) +{ + int unused; + + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: + mutex_unlock(&meye.lock); + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + fallthrough; + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused, + sizeof(int), &meye.doneq_lock) != sizeof(int)) + break; + } + *i = meye.grab_buffer[*i].size; + mutex_unlock(&meye.lock); + return 0; +} + +static int meyeioc_stillcapt(void) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + mchip_take_picture(); + + mchip_get_picture(meye.grab_fbuffer, + mchip_hsize() * mchip_vsize() * 2); + + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_stilljcapt(int *len) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + *len = -1; + + while (*len == -1) { + mchip_take_picture(); + *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); + } + + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "meye", sizeof(cap->driver)); + strscpy(cap->card, "meye", sizeof(cap->card)); + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strscpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i != 0) + return -EINVAL; + + return 0; +} + +static int meye_s_ctrl(struct v4l2_ctrl *ctrl) +{ + mutex_lock(&meye.lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, ctrl->val); + meye.brightness = ctrl->val << 10; + break; + case V4L2_CID_HUE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, ctrl->val); + meye.hue = ctrl->val << 10; + break; + case V4L2_CID_CONTRAST: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, ctrl->val); + meye.contrast = ctrl->val << 10; + break; + case V4L2_CID_SATURATION: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, ctrl->val); + meye.colour = ctrl->val << 10; + break; + case V4L2_CID_MEYE_AGC: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, ctrl->val); + meye.params.agc = ctrl->val; + break; + case V4L2_CID_SHARPNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, ctrl->val); + meye.params.sharpness = ctrl->val; + break; + case V4L2_CID_MEYE_PICTURE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, ctrl->val); + meye.params.picture = ctrl->val; + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + meye.params.quality = ctrl->val; + break; + case V4L2_CID_MEYE_FRAMERATE: + meye.params.framerate = ctrl->val; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; + + if (f->index == 0) { + /* standard YUV 422 capture */ + f->flags = 0; + f->pixelformat = V4L2_PIX_FMT_YUYV; + } else { + /* compressed MJPEG capture */ + f->pixelformat = V4L2_PIX_FMT_MJPEG; + } + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + } + + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + default: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MCHIP_HIC_MODE_CONT_COMP: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + break; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = mchip_hsize(); + f->fmt.pix.height = mchip_vsize(); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + mutex_lock(&meye.lock); + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + meye.params.subsample = 1; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + meye.params.subsample = 0; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + break; + case V4L2_PIX_FMT_MJPEG: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + break; + } + + mutex_unlock(&meye.lock); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int i; + + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (meye.grab_fbuffer && req->count == gbuffers) { + /* already allocated, no modifications */ + return 0; + } + + mutex_lock(&meye.lock); + if (meye.grab_fbuffer) { + for (i = 0; i < gbuffers; i++) + if (meye.vma_use_count[i]) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + rvfree(meye.grab_fbuffer, gbuffers * gbufsize); + meye.grab_fbuffer = NULL; + } + + gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); + req->count = gbuffers; + meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); + + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + unsigned int index = buf->index; + + if (index >= gbuffers) + return -EINVAL; + + buf->bytesused = meye.grab_buffer[index].size; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + if (meye.grab_buffer[index].state == MEYE_BUF_USING) + buf->flags |= V4L2_BUF_FLAG_QUEUED; + + if (meye.grab_buffer[index].state == MEYE_BUF_DONE) + buf->flags |= V4L2_BUF_FLAG_DONE; + + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, meye.grab_buffer[index].ts); + buf->sequence = meye.grab_buffer[index].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * gbufsize; + buf->length = gbufsize; + + return 0; +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index >= gbuffers) + return -EINVAL; + + if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) + return -EINVAL; + + mutex_lock(&meye.lock); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + meye.grab_buffer[buf->index].state = MEYE_BUF_USING; + kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index, + sizeof(int), &meye.grabq_lock); + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int reqnr; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + + if (wait_event_interruptible(meye.proc_list, + kfifo_len(&meye.doneq) != 0) < 0) { + mutex_unlock(&meye.lock); + return -EINTR; + } + + if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr, + sizeof(int), &meye.doneq_lock)) { + mutex_unlock(&meye.lock); + return -EBUSY; + } + + if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + + buf->index = reqnr; + buf->bytesused = meye.grab_buffer[reqnr].size; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, meye.grab_buffer[reqnr].ts); + buf->sequence = meye.grab_buffer[reqnr].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = reqnr * gbufsize; + buf->length = gbufsize; + meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + mchip_hic_stop(); + kfifo_reset(&meye.grabq); + kfifo_reset(&meye.doneq); + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + + mutex_unlock(&meye.lock); + return 0; +} + +static long vidioc_default(struct file *file, void *fh, bool valid_prio, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case MEYEIOC_G_PARAMS: + return meyeioc_g_params((struct meye_params *) arg); + + case MEYEIOC_S_PARAMS: + return meyeioc_s_params((struct meye_params *) arg); + + case MEYEIOC_QBUF_CAPT: + return meyeioc_qbuf_capt((int *) arg); + + case MEYEIOC_SYNC: + return meyeioc_sync(file, fh, (int *) arg); + + case MEYEIOC_STILLCAPT: + return meyeioc_stillcapt(); + + case MEYEIOC_STILLJCAPT: + return meyeioc_stilljcapt((int *) arg); + + default: + return -ENOTTY; + } + +} + +static __poll_t meye_poll(struct file *file, poll_table *wait) +{ + __poll_t res = v4l2_ctrl_poll(file, wait); + + mutex_lock(&meye.lock); + poll_wait(file, &meye.proc_list, wait); + if (kfifo_len(&meye.doneq)) + res |= EPOLLIN | EPOLLRDNORM; + mutex_unlock(&meye.lock); + return res; +} + +static void meye_vm_open(struct vm_area_struct *vma) +{ + long idx = (long)vma->vm_private_data; + meye.vma_use_count[idx]++; +} + +static void meye_vm_close(struct vm_area_struct *vma) +{ + long idx = (long)vma->vm_private_data; + meye.vma_use_count[idx]--; +} + +static const struct vm_operations_struct meye_vm_ops = { + .open = meye_vm_open, + .close = meye_vm_close, +}; + +static int meye_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + mutex_lock(&meye.lock); + if (size > gbuffers * gbufsize || offset > gbuffers * gbufsize - size) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + if (!meye.grab_fbuffer) { + int i; + + /* lazy allocation */ + meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize); + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + } + pos = (unsigned long)meye.grab_fbuffer + offset; + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_ops = &meye_vm_ops; + vma->vm_flags &= ~VM_IO; /* not I/O memory */ + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = (void *) (offset / gbufsize); + meye_vm_open(vma); + + mutex_unlock(&meye.lock); + return 0; +} + +static const struct v4l2_file_operations meye_fops = { + .owner = THIS_MODULE, + .open = meye_open, + .release = meye_release, + .mmap = meye_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = meye_poll, +}; + +static const struct v4l2_ioctl_ops meye_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_default = vidioc_default, +}; + +static const struct video_device meye_template = { + .name = "meye", + .fops = &meye_fops, + .ioctl_ops = &meye_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_ctrl_ops meye_ctrl_ops = { + .s_ctrl = meye_s_ctrl, +}; + +static int __maybe_unused meye_suspend(struct device *dev) +{ + meye.pm_mchip_mode = meye.mchip_mode; + mchip_hic_stop(); + mchip_set(MCHIP_MM_INTA, 0x0); + return 0; +} + +static int __maybe_unused meye_resume(struct device *dev) +{ + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + switch (meye.pm_mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + } + return 0; +} + +static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + static const struct v4l2_ctrl_config ctrl_agc = { + .id = V4L2_CID_MEYE_AGC, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "AGC", + .max = 63, + .step = 1, + .def = 48, + .flags = V4L2_CTRL_FLAG_SLIDER, + }; + static const struct v4l2_ctrl_config ctrl_picture = { + .id = V4L2_CID_MEYE_PICTURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Picture", + .max = 63, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_framerate = { + .id = V4L2_CID_MEYE_FRAMERATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Framerate", + .max = 31, + .step = 1, + }; + struct v4l2_device *v4l2_dev = &meye.v4l2_dev; + int ret = -EBUSY; + unsigned long mchip_adr; + + if (meye.mchip_dev != NULL) { + printk(KERN_ERR "meye: only one device allowed!\n"); + return ret; + } + + ret = v4l2_device_register(&pcidev->dev, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } + ret = -ENOMEM; + meye.mchip_dev = pcidev; + + meye.grab_temp = vmalloc(array_size(PAGE_SIZE, MCHIP_NB_PAGES_MJPEG)); + if (!meye.grab_temp) + goto outvmalloc; + + spin_lock_init(&meye.grabq_lock); + if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, + GFP_KERNEL)) + goto outkfifoalloc1; + + spin_lock_init(&meye.doneq_lock); + if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, + GFP_KERNEL)) + goto outkfifoalloc2; + + meye.vdev = meye_template; + meye.vdev.v4l2_dev = &meye.v4l2_dev; + + ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1); + if (ret) { + v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); + v4l2_err(v4l2_dev, "meye: did you enable the camera in sonypi using the module options ?\n"); + goto outsonypienable; + } + + ret = pci_enable_device(meye.mchip_dev); + if (ret) { + v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); + goto outenabledev; + } + + ret = -EIO; + mchip_adr = pci_resource_start(meye.mchip_dev,0); + if (!mchip_adr) { + v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); + goto outregions; + } + if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0), + "meye")) { + v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); + goto outregions; + } + meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); + if (!meye.mchip_mmregs) { + v4l2_err(v4l2_dev, "meye: ioremap failed\n"); + goto outremap; + } + + meye.mchip_irq = pcidev->irq; + if (request_irq(meye.mchip_irq, meye_irq, + IRQF_SHARED, "meye", meye_irq)) { + v4l2_err(v4l2_dev, "request_irq failed\n"); + goto outreqirq; + } + + pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8); + pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64); + + pci_set_master(meye.mchip_dev); + + /* Ask the camera to perform a soft reset. */ + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + mutex_init(&meye.lock); + init_waitqueue_head(&meye.proc_list); + + v4l2_ctrl_handler_init(&meye.hdl, 3); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_HUE, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_agc, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_picture, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, 10, 1, 8); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_framerate, NULL); + if (meye.hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + goto outvideoreg; + } + + v4l2_ctrl_handler_setup(&meye.hdl); + meye.vdev.ctrl_handler = &meye.hdl; + + if (video_register_device(&meye.vdev, VFL_TYPE_VIDEO, + video_nr) < 0) { + v4l2_err(v4l2_dev, "video_register_device failed\n"); + goto outvideoreg; + } + + v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", + MEYE_DRIVER_VERSION); + v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", + meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); + + return 0; + +outvideoreg: + v4l2_ctrl_handler_free(&meye.hdl); + free_irq(meye.mchip_irq, meye_irq); +outreqirq: + iounmap(meye.mchip_mmregs); +outremap: + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); +outregions: + pci_disable_device(meye.mchip_dev); +outenabledev: + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); +outsonypienable: + kfifo_free(&meye.doneq); +outkfifoalloc2: + kfifo_free(&meye.grabq); +outkfifoalloc1: + vfree(meye.grab_temp); +outvmalloc: + return ret; +} + +static void meye_remove(struct pci_dev *pcidev) +{ + video_unregister_device(&meye.vdev); + + mchip_hic_stop(); + + mchip_dma_free(); + + /* disable interrupts */ + mchip_set(MCHIP_MM_INTA, 0x0); + + free_irq(meye.mchip_irq, meye_irq); + + iounmap(meye.mchip_mmregs); + + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); + + pci_disable_device(meye.mchip_dev); + + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); + + kfifo_free(&meye.doneq); + kfifo_free(&meye.grabq); + + vfree(meye.grab_temp); + + if (meye.grab_fbuffer) { + rvfree(meye.grab_fbuffer, gbuffers*gbufsize); + meye.grab_fbuffer = NULL; + } + + printk(KERN_INFO "meye: removed\n"); +} + +static const struct pci_device_id meye_pci_tbl[] = { + { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, meye_pci_tbl); + +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); + +static struct pci_driver meye_driver = { + .name = "meye", + .id_table = meye_pci_tbl, + .probe = meye_probe, + .remove = meye_remove, + .driver.pm = &meye_pm_ops, +}; + +static int __init meye_init(void) +{ + gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS)); + if (gbufsize > MEYE_MAX_BUFSIZE) + gbufsize = MEYE_MAX_BUFSIZE; + gbufsize = PAGE_ALIGN(gbufsize); + printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) for capture\n", + gbuffers, + gbufsize / 1024, gbuffers * gbufsize / 1024); + return pci_register_driver(&meye_driver); +} + +static void __exit meye_exit(void) +{ + pci_unregister_driver(&meye_driver); +} + +module_init(meye_init); +module_exit(meye_exit); diff --git a/drivers/staging/media/deprecated/meye/meye.h b/drivers/staging/media/deprecated/meye/meye.h new file mode 100644 index 000000000000..5fa6552cf93d --- /dev/null +++ b/drivers/staging/media/deprecated/meye/meye.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve + * + * Copyright (C) 2000 Andrew Tridgell + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + */ + +#ifndef _MEYE_PRIV_H_ +#define _MEYE_PRIV_H_ + +#define MEYE_DRIVER_MAJORVERSION 1 +#define MEYE_DRIVER_MINORVERSION 14 + +#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ + __stringify(MEYE_DRIVER_MINORVERSION) + +#include +#include +#include +#include + +/****************************************************************************/ +/* Motion JPEG chip registers */ +/****************************************************************************/ + +/* Motion JPEG chip PCI configuration registers */ +#define MCHIP_PCI_POWER_CSR 0x54 +#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */ +#define MCHIP_PCI_HOSTUSEREQ_SET 0x64 +#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68 +#define MCHIP_PCI_LOWPOWER_SET 0x6c +#define MCHIP_PCI_LOWPOWER_CLR 0x70 +#define MCHIP_PCI_SOFTRESET_SET 0x74 + +/* Motion JPEG chip memory mapped registers */ +#define MCHIP_MM_REGS 0x200 /* 512 bytes */ +#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */ +#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */ + +#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */ +#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */ +#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */ +#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */ + +#define MCHIP_MM_INTA 0x04 /* Int status/mask */ +#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */ +#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */ +#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */ +#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */ +#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */ +#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800 +#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */ +#define MCHIP_MM_INTA_JPEG_MASK 0x00001000 +#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */ +#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */ +#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000 + +#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/ + /* n*4kB */ +#define MCHIP_NB_PAGES 1024 /* pages for display */ +#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */ + +#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */ +#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */ +#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */ +#define MCHIP_MM_FIR_FAILFR_SHIFT 27 + + /* continuous comp/decomp mode */ +#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */ +#define MCHIP_MM_FIR_C_ENDL_SHIFT 1 +#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */ +#define MCHIP_MM_FIR_C_ENDP_SHIFT 11 +#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */ +#define MCHIP_MM_FIR_C_STARTP_SHIFT 19 + + /* continuous picture output mode */ +#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */ +#define MCHIP_MM_FIR_O_STARTP_SHIFT 17 + +#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */ +#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */ +#define MCHIP_MM_FIFO_MASK 0x00000003 +#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/ +#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */ +#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */ +#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */ +#define MCHIP_MM_FIFO_READY 0x3 /* data ready */ + +#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */ + +#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */ + +#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */ + +#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */ + +#define MCHIP_HIC_CTL 0x50 /* HIC control */ +#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */ +#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */ + +#define MCHIP_HIC_CMD 0x54 /* HIC command */ +#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/ +#define MCHIP_HIC_CMD_NOOP 0x0 +#define MCHIP_HIC_CMD_START 0x1 +#define MCHIP_HIC_CMD_STOP 0x2 + +#define MCHIP_HIC_MODE 0x58 +#define MCHIP_HIC_MODE_NOOP 0x0 +#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */ +#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */ +#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */ +#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */ +#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */ +#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */ +#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */ +#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */ + +#define MCHIP_HIC_STATUS 0x5c +#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */ +#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */ +#define MCHIP_HIC_STATUS_IDLE 0x00000003 +#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */ +#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */ +#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */ + +#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */ + +#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */ +#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */ + /* 1: Y' V Y U */ + +#define MCHIP_MCC_CMD 0x80 /* MCC commands */ +#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */ +#define MCHIP_MCC_CMD_IIC_START_SET 0x1 +#define MCHIP_MCC_CMD_IIC_END_SET 0x2 +#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */ +#define MCHIP_MCC_CMD_FM_READ 0x4 +#define MCHIP_MCC_CMD_FM_STOP 0x5 +#define MCHIP_MCC_CMD_CAPTURE 0x6 +#define MCHIP_MCC_CMD_DISPLAY 0x7 +#define MCHIP_MCC_CMD_END_DISP 0x8 +#define MCHIP_MCC_CMD_STILL_COMP 0x9 +#define MCHIP_MCC_CMD_STILL_DECOMP 0xa +#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb +#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc +#define MCHIP_MCC_CMD_CONT_COMP 0xd +#define MCHIP_MCC_CMD_CONT_DECOMP 0xe +#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */ + +#define MCHIP_MCC_IIC_WR 0x84 + +#define MCHIP_MCC_MCC_WR 0x88 + +#define MCHIP_MCC_MCC_RD 0x8c + +#define MCHIP_MCC_STATUS 0x90 +#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */ +#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */ +#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */ +#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */ +#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */ +#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */ +#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */ +#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */ + +#define MCHIP_MCC_SIG_POLARITY 0x94 +#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */ +#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */ +#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */ + +#define MCHIP_MCC_IRQ 0x98 +#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */ +#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010 +#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */ +#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020 +#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */ +#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040 +#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */ +#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080 + +#define MCHIP_MCC_HSTART 0x9c /* video in */ +#define MCHIP_MCC_VSTART 0xa0 +#define MCHIP_MCC_HCOUNT 0xa4 +#define MCHIP_MCC_VCOUNT 0xa8 +#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */ +#define MCHIP_MCC_R_YBASE 0xb0 +#define MCHIP_MCC_R_XRANGE 0xb4 +#define MCHIP_MCC_R_YRANGE 0xb8 +#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */ +#define MCHIP_MCC_B_YBASE 0xc0 +#define MCHIP_MCC_B_XRANGE 0xc4 +#define MCHIP_MCC_B_YRANGE 0xc8 + +#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */ + +#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */ + +/* VRJ registers (see table 12.2.4) */ +#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0 +#define MCHIP_VRJ_PIXEL_DATA 0x1b8 + +#define MCHIP_VRJ_BUS_MODE 0x100 +#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108 +#define MCHIP_VRJ_PDAT_USE 0x110 +#define MCHIP_VRJ_MODE_SPECIFY 0x118 +#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120 +#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124 +#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128 +#define MCHIP_VRJ_TABLE_DATA 0x140 +#define MCHIP_VRJ_RESTART_INTERVAL 0x148 +#define MCHIP_VRJ_NUM_LINES 0x150 +#define MCHIP_VRJ_NUM_PIXELS 0x158 +#define MCHIP_VRJ_NUM_COMPONENTS 0x160 +#define MCHIP_VRJ_SOF1 0x168 +#define MCHIP_VRJ_SOF2 0x170 +#define MCHIP_VRJ_SOF3 0x178 +#define MCHIP_VRJ_SOF4 0x180 +#define MCHIP_VRJ_SOS 0x188 +#define MCHIP_VRJ_SOFT_RESET 0x190 + +#define MCHIP_VRJ_STATUS 0x1c0 +#define MCHIP_VRJ_STATUS_BUSY 0x00001 +#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002 +#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004 +#define MCHIP_VRJ_STATUS_ERROR 0x00008 + +#define MCHIP_VRJ_IRQ_FLAG 0x1c8 +#define MCHIP_VRJ_ERROR_REPORT 0x1d8 + +#define MCHIP_VRJ_START_COMMAND 0x1a0 + +/****************************************************************************/ +/* Driver definitions. */ +/****************************************************************************/ + +/* Sony Programmable I/O Controller for accessing the camera commands */ +#include + +/* private API definitions */ +#include +#include + + +/* Enable jpg software correction */ +#define MEYE_JPEG_CORRECTION 1 + +/* Maximum size of a buffer */ +#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */ + +/* Maximum number of buffers */ +#define MEYE_MAX_BUFNBRS 32 + +/* State of a buffer */ +#define MEYE_BUF_UNUSED 0 /* not used */ +#define MEYE_BUF_USING 1 /* currently grabbing / playing */ +#define MEYE_BUF_DONE 2 /* done */ + +/* grab buffer */ +struct meye_grab_buffer { + int state; /* state of buffer */ + unsigned long size; /* size of jpg frame */ + u64 ts; /* timestamp */ + unsigned long sequence; /* sequence number */ +}; + +/* size of kfifos containing buffer indices */ +#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS + +/* Motion Eye device structure */ +struct meye { + struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ + struct v4l2_ctrl_handler hdl; + struct pci_dev *mchip_dev; /* pci device */ + u8 mchip_irq; /* irq */ + u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ + u8 mchip_fnum; /* current mchip frame number */ + unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ + u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ + void *mchip_ptable_toc; /* mchip: ptable toc */ + dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ + unsigned char *grab_fbuffer; /* capture framebuffer */ + unsigned char *grab_temp; /* temporary buffer */ + /* list of buffers */ + struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; + int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ + struct mutex lock; /* mutex for open/mmap... */ + struct kfifo grabq; /* queue for buffers to be grabbed */ + spinlock_t grabq_lock; /* lock protecting the queue */ + struct kfifo doneq; /* queue for grabbed buffers */ + spinlock_t doneq_lock; /* lock protecting the queue */ + wait_queue_head_t proc_list; /* wait queue */ + struct video_device vdev; /* video device parameters */ + u16 brightness; + u16 hue; + u16 contrast; + u16 colour; + struct meye_params params; /* additional parameters */ + unsigned long in_use; /* set to 1 if the device is in use */ + u8 pm_mchip_mode; /* old mchip mode */ +}; + +#endif -- cgit v1.3-14-g43fede From 50f0b24381665ad3233dd586e069e2c3ae7b1d9b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:44 +0200 Subject: media: zr364xx: deprecate this driver Deprecate the zr364xx driver. This driver does not use the vb2 framework for video streaming, instead it uses the old videobuf framework. We want to get rid of these old drivers, so deprecated it for future removal. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/usb/Kconfig | 1 - drivers/media/usb/Makefile | 1 - drivers/media/usb/zr364xx/Kconfig | 15 - drivers/media/usb/zr364xx/Makefile | 3 - drivers/media/usb/zr364xx/zr364xx.c | 1635 -------------------- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/deprecated/zr364xx/Kconfig | 18 + drivers/staging/media/deprecated/zr364xx/Makefile | 3 + drivers/staging/media/deprecated/zr364xx/TODO | 7 + drivers/staging/media/deprecated/zr364xx/zr364xx.c | 1635 ++++++++++++++++++++ 12 files changed, 1666 insertions(+), 1656 deletions(-) delete mode 100644 drivers/media/usb/zr364xx/Kconfig delete mode 100644 drivers/media/usb/zr364xx/Makefile delete mode 100644 drivers/media/usb/zr364xx/zr364xx.c create mode 100644 drivers/staging/media/deprecated/zr364xx/Kconfig create mode 100644 drivers/staging/media/deprecated/zr364xx/Makefile create mode 100644 drivers/staging/media/deprecated/zr364xx/TODO create mode 100644 drivers/staging/media/deprecated/zr364xx/zr364xx.c diff --git a/MAINTAINERS b/MAINTAINERS index 47b9118ee992..e26da9b8e277 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21218,7 +21218,7 @@ S: Maintained W: http://royale.zerezo.com/zr364xx/ T: git git://linuxtv.org/media_tree.git F: Documentation/admin-guide/media/zr364xx* -F: drivers/media/usb/zr364xx/ +F: drivers/staging/media/deprecated/zr364xx/ USER-MODE LINUX (UML) M: Richard Weinberger diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index afbb8dd28b5b..3d0138f8573c 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -18,7 +18,6 @@ source "drivers/media/usb/pwc/Kconfig" source "drivers/media/usb/s2255/Kconfig" source "drivers/media/usb/usbtv/Kconfig" source "drivers/media/usb/uvc/Kconfig" -source "drivers/media/usb/zr364xx/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index fa8e16ff9b03..7fccc6604b1f 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -12,7 +12,6 @@ obj-y += s2255/ obj-y += siano/ obj-y += ttusb-budget/ obj-y += ttusb-dec/ -obj-y += zr364xx/ # Please keep it alphabetically sorted by Kconfig name # (e. g. LC_ALL=C sort Makefile) diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig deleted file mode 100644 index a9fb02566c4b..000000000000 --- a/drivers/media/usb/zr364xx/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config USB_ZR364XX - tristate "USB ZR364XX Camera support" - depends on VIDEO_DEV - select VIDEOBUF_GEN - select VIDEOBUF_VMALLOC - help - Say Y here if you want to connect this type of camera to your - computer's USB port. - See for more info - and list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called zr364xx. - diff --git a/drivers/media/usb/zr364xx/Makefile b/drivers/media/usb/zr364xx/Makefile deleted file mode 100644 index edab017d499c..000000000000 --- a/drivers/media/usb/zr364xx/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_USB_ZR364XX) += zr364xx.o - diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c deleted file mode 100644 index 538a330046ec..000000000000 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ /dev/null @@ -1,1635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran 364xx based USB webcam module version 0.73 - * - * Allows you to use your USB webcam with V4L2 applications - * This is still in heavy development ! - * - * Copyright (C) 2004 Antoine Jacquet - * http://royale.zerezo.com/zr364xx/ - * - * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers - * V4L2 version inspired by meye.c driver - * - * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Version Information */ -#define DRIVER_VERSION "0.7.4" -#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" -#define DRIVER_DESC "Zoran 364xx" - - -/* Camera */ -#define FRAMES 1 -#define MAX_FRAME_SIZE 200000 -#define BUFFER_SIZE 0x1000 -#define CTRL_TIMEOUT 500 - -#define ZR364XX_DEF_BUFS 4 -#define ZR364XX_READ_IDLE 0 -#define ZR364XX_READ_FRAME 1 - -/* Debug macro */ -#define DBG(fmt, args...) \ - do { \ - if (debug) { \ - printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ - } \ - } while (0) - -/*#define FULL_DEBUG 1*/ -#ifdef FULL_DEBUG -#define _DBG DBG -#else -#define _DBG(fmt, args...) -#endif - -/* Init methods, need to find nicer names for these - * the exact names of the chipsets would be the best if someone finds it */ -#define METHOD0 0 -#define METHOD1 1 -#define METHOD2 2 -#define METHOD3 3 - - -/* Module parameters */ -static int debug; -static int mode; - - -/* Module parameters interface */ -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level"); -module_param(mode, int, 0644); -MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); - - -/* Devices supported by this driver - * .driver_info contains the init method used by the camera */ -static const struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, - {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, - {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, - {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, - {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, - {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, - {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, - {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, - {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, - {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, - {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, - {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, - {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, - {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, - {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* frame structure */ -struct zr364xx_framei { - unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, - ZR364XX_READ_FRAME */ - void *lpvbits; /* image data */ - unsigned long cur_size; /* current data copied to it */ -}; - -/* image buffer structure */ -struct zr364xx_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ - struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ -}; - -struct zr364xx_dmaqueue { - struct list_head active; - struct zr364xx_camera *cam; -}; - -struct zr364xx_pipeinfo { - u32 transfer_size; - u8 *transfer_buffer; - u32 state; - void *stream_urb; - void *cam; /* back pointer to zr364xx_camera struct */ - u32 err_count; - u32 idx; -}; - -struct zr364xx_fmt { - u32 fourcc; - int depth; -}; - -/* image formats. */ -static const struct zr364xx_fmt formats[] = { - { - .fourcc = V4L2_PIX_FMT_JPEG, - .depth = 24 - } -}; - -/* Camera stuff */ -struct zr364xx_camera { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface;/* the interface for this device */ - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; /* v4l video device */ - struct v4l2_fh *owner; /* owns the streaming */ - int nb; - struct zr364xx_bufferi buffer; - int skip; - int width; - int height; - int method; - struct mutex lock; - - spinlock_t slock; - struct zr364xx_dmaqueue vidq; - int last_frame; - int cur_frame; - unsigned long frame_count; - int b_acquire; - struct zr364xx_pipeinfo pipe[1]; - - u8 read_endpoint; - - const struct zr364xx_fmt *fmt; - struct videobuf_queue vb_vidq; - bool was_streaming; -}; - -/* buffer for one video frame */ -struct zr364xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - const struct zr364xx_fmt *fmt; -}; - -/* function used to send initialisation commands to the camera */ -static int send_control_msg(struct usb_device *udev, u8 request, u16 value, - u16 index, unsigned char *cp, u16 size) -{ - int status; - - unsigned char *transfer_buffer = kmemdup(cp, size, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; - - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - transfer_buffer, size, CTRL_TIMEOUT); - - kfree(transfer_buffer); - return status; -} - - -/* Control messages sent to the camera to initialize it - * and launch the capture */ -typedef struct { - unsigned int value; - unsigned int size; - unsigned char *bytes; -} message; - -/* method 0 */ -static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d3[] = { 0, 0 }; -static message m0[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0x3370, sizeof(m0d1), m0d1}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2610, sizeof(m0d2), m0d2}, - {0xe107, 0, NULL}, - {0x2502, 0, NULL}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m0d3), m0d3}, - {-1, -1, NULL} -}; - -/* method 1 */ -static unsigned char m1d1[] = { 0xff, 0xff }; -static unsigned char m1d2[] = { 0x00, 0x00 }; -static message m1[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m1d1), m1d1}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m1d2), m1d2}, - {-1, -1, NULL} -}; - -/* method 2 */ -static unsigned char m2d1[] = { 0xff, 0xff }; -static message m2[] = { - {0x1f30, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m2d1), m2d1}, - {0x1f70, 0, NULL}, - {-1, -1, NULL} -}; - -/* init table */ -static message *init[4] = { m0, m1, m2, m2 }; - - -/* JPEG static data in header (Huffman table, etc) */ -static unsigned char header1[] = { - 0xFF, 0xD8, - /* - 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', - 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, - */ - 0xFF, 0xDB, 0x00, 0x84 -}; -static unsigned char header2[] = { - 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, - 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, - 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, - 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, - 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, - 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, - 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, - 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, - 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, - 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, - 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, - 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, - 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, - 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00 -}; -static unsigned char header3; - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct zr364xx_camera *cam = vq->priv_data; - - *size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (*count == 0) - *count = ZR364XX_DEF_BUFS; - - if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) - *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) -{ - _DBG("%s\n", __func__); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct zr364xx_camera *cam = vq->priv_data; - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - int rc; - - DBG("%s, field=%d\n", __func__, field); - if (!cam->fmt) - return -EINVAL; - - buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { - DBG("invalid buffer prepare\n"); - return -EINVAL; - } - - buf->fmt = cam->fmt; - buf->vb.width = cam->width; - buf->vb.height = cam->height; - buf->vb.field = field; - - if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - struct zr364xx_camera *cam = vq->priv_data; - - _DBG("%s\n", __func__); - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &cam->vidq.active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - - _DBG("%s\n", __func__); - free_buffer(vq, buf); -} - -static const struct videobuf_queue_ops zr364xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************/ -/* V4L2 integration */ -/********************/ -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type); - -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, - loff_t * ppos) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err = 0; - - _DBG("%s\n", __func__); - - if (!buf) - return -EINVAL; - - if (!count) - return -EINVAL; - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = zr364xx_vidioc_streamon(file, file->private_data, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (err == 0) { - DBG("%s: reading %d bytes at pos %d.\n", __func__, - (int) count, (int) *ppos); - - /* NoMan Sux ! */ - err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - } - mutex_unlock(&cam->lock); - return err; -} - -/* video buffer vmalloc implementation based partly on VIVI driver which is - * Copyright (c) 2006 by - * Mauro Carvalho Chehab - * Ted Walther - * John Sokol - * http://v4l.videotechnology.com/ - * - */ -static void zr364xx_fillbuff(struct zr364xx_camera *cam, - struct zr364xx_buffer *buf, - int jpgsize) -{ - int pos = 0; - const char *tmpbuf; - char *vbuf = videobuf_to_vmalloc(&buf->vb); - unsigned long last_frame; - - if (!vbuf) - return; - - last_frame = cam->last_frame; - if (last_frame != -1) { - tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; - switch (buf->fmt->fourcc) { - case V4L2_PIX_FMT_JPEG: - buf->vb.size = jpgsize; - memcpy(vbuf, tmpbuf, buf->vb.size); - break; - default: - printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); - } - cam->last_frame = -1; - } else { - printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); - return; - } - DBG("%s: Buffer %p size= %d\n", __func__, vbuf, pos); - /* tell v4l buffer was filled */ - - buf->vb.field_count = cam->frame_count * 2; - buf->vb.ts = ktime_get_ns(); - buf->vb.state = VIDEOBUF_DONE; -} - -static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) -{ - struct zr364xx_dmaqueue *dma_q = &cam->vidq; - struct zr364xx_buffer *buf; - unsigned long flags = 0; - int rc = 0; - - DBG("wakeup: %p\n", &dma_q); - spin_lock_irqsave(&cam->slock, flags); - - if (list_empty(&dma_q->active)) { - DBG("No active queue to serve\n"); - rc = -1; - goto unlock; - } - buf = list_entry(dma_q->active.next, - struct zr364xx_buffer, vb.queue); - - if (!waitqueue_active(&buf->vb.done)) { - /* no one active */ - rc = -1; - goto unlock; - } - list_del(&buf->vb.queue); - 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); - DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); -unlock: - spin_unlock_irqrestore(&cam->slock, flags); - return rc; -} - -/* this function moves the usb stream read pipe data - * into the system buffers. - * returns 0 on success, EAGAIN if more data to process (call this - * function again). - */ -static int zr364xx_read_video_callback(struct zr364xx_camera *cam, - struct zr364xx_pipeinfo *pipe_info, - struct urb *purb) -{ - unsigned char *pdest; - unsigned char *psrc; - s32 idx = cam->cur_frame; - struct zr364xx_framei *frm = &cam->buffer.frame[idx]; - int i = 0; - unsigned char *ptr = NULL; - - _DBG("buffer to user\n"); - - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16 *)pipe_info->transfer_buffer; - for (i = 0; i < purb->actual_length/2; i++) - swab16s(buf + i); - } - - /* search done. now find out if should be acquiring */ - if (!cam->b_acquire) { - /* we found a frame, but this channel is turned off */ - frm->ulState = ZR364XX_READ_IDLE; - return -EINVAL; - } - - psrc = (u8 *)pipe_info->transfer_buffer; - ptr = pdest = frm->lpvbits; - - if (frm->ulState == ZR364XX_READ_IDLE) { - if (purb->actual_length < 128) { - /* header incomplete */ - dev_info(&cam->udev->dev, - "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n", - __func__, purb->actual_length); - return -EINVAL; - } - - frm->ulState = ZR364XX_READ_FRAME; - frm->cur_size = 0; - - _DBG("jpeg header, "); - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, psrc + 128, - purb->actual_length - 128); - ptr += purb->actual_length - 128; - _DBG("header : %d %d %d %d %d %d %d %d %d\n", - psrc[0], psrc[1], psrc[2], - psrc[3], psrc[4], psrc[5], - psrc[6], psrc[7], psrc[8]); - frm->cur_size = ptr - pdest; - } else { - if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { - dev_info(&cam->udev->dev, - "%s: buffer (%d bytes) too small to hold frame data. Discarding frame data.\n", - __func__, MAX_FRAME_SIZE); - } else { - pdest += frm->cur_size; - memcpy(pdest, psrc, purb->actual_length); - frm->cur_size += purb->actual_length; - } - } - /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, - purb->actual_length);*/ - - if (purb->actual_length < pipe_info->transfer_size) { - _DBG("****************Buffer[%d]full*************\n", idx); - cam->last_frame = cam->cur_frame; - cam->cur_frame++; - /* end of system frame ring buffer, start at zero */ - if (cam->cur_frame == cam->buffer.dwFrames) - cam->cur_frame = 0; - - /* frame ready */ - /* go back to find the JPEG EOI marker */ - ptr = pdest = frm->lpvbits; - ptr += frm->cur_size - 2; - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == pdest) - DBG("No EOI marker\n"); - - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr != pdest) { - DBG("Bogus frame ? %d\n", ++(cam->nb)); - } else if (cam->b_acquire) { - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) - cam->skip--; - else { - _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", - frm->cur_size, - pdest[0], pdest[1], pdest[2], pdest[3], - pdest[4], pdest[5], pdest[6], pdest[7]); - - zr364xx_got_frame(cam, frm->cur_size); - } - } - cam->frame_count++; - frm->ulState = ZR364XX_READ_IDLE; - frm->cur_size = 0; - } - /* done successfully */ - return 0; -} - -static int zr364xx_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - strscpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - if (cam->udev->product) - strscpy(cap->card, cam->udev->product, sizeof(cap->card)); - strscpy(cap->bus_info, dev_name(&cam->udev->dev), - sizeof(cap->bus_info)); - return 0; -} - -static int zr364xx_vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strscpy(i->name, DRIVER_DESC " Camera", sizeof(i->name)); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int zr364xx_vidioc_g_input(struct file *file, void *priv, - unsigned int *i) -{ - *i = 0; - return 0; -} - -static int zr364xx_vidioc_s_input(struct file *file, void *priv, - unsigned int i) -{ - if (i != 0) - return -EINVAL; - return 0; -} - -static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct zr364xx_camera *cam = - container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); - int temp; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - /* hardware brightness */ - send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - ctrl->val; - send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, - void *priv, struct v4l2_fmtdesc *f) -{ - if (f->index > 0) - return -EINVAL; - f->pixelformat = formats[0].fourcc; - return 0; -} - -static char *decode_fourcc(__u32 pixelformat, char *buf) -{ - buf[0] = pixelformat & 0xff; - buf[1] = (pixelformat >> 8) & 0xff; - buf[2] = (pixelformat >> 16) & 0xff; - buf[3] = (pixelformat >> 24) & 0xff; - buf[4] = '\0'; - return buf; -} - -static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - char pixelformat_name[5]; - - if (!cam) - return -ENODEV; - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { - DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); - return -EINVAL; - } - - if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && - !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return 0; -} - -static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam; - - if (!file) - return -ENODEV; - cam = video_drvdata(file); - - f->fmt.pix.pixelformat = formats[0].fourcc; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - return 0; -} - -static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - char pixelformat_name[5]; - int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); - int i; - - if (ret < 0) - return ret; - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&cam->vb_vidq)) { - DBG("%s queue busy\n", __func__); - ret = -EBUSY; - goto out; - } - - if (cam->owner) { - DBG("%s can't change format after started\n", __func__); - ret = -EBUSY; - goto out; - } - - cam->width = f->fmt.pix.width; - cam->height = f->fmt.pix.height; - DBG("%s: %dx%d mode selected\n", __func__, - cam->width, cam->height); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - cam->vb_vidq.field = f->fmt.pix.field; - - if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) - mode = 1; - else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) - mode = 2; - else - mode = 0; - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - for (i = 0; init[cam->method][i].size != -1; i++) { - ret = - send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (ret < 0) { - dev_err(&cam->udev->dev, - "error during resolution change sequence: %d\n", i); - goto out; - } - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - cam->skip = 2; - ret = 0; - -out: - mutex_unlock(&q->vb_lock); - - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return ret; -} - -static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - if (cam->owner && cam->owner != priv) - return -EBUSY; - return videobuf_reqbufs(&cam->vb_vidq, p); -} - -static int zr364xx_vidioc_querybuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - rc = videobuf_querybuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_qbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_qbuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_dqbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); - return rc; -} - -static void read_pipe_completion(struct urb *purb) -{ - struct zr364xx_pipeinfo *pipe_info; - struct zr364xx_camera *cam; - int pipe; - - pipe_info = purb->context; - _DBG("%s %p, status %d\n", __func__, purb, purb->status); - if (!pipe_info) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - cam = pipe_info->cam; - if (!cam) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - /* if shutting down, do not resubmit, exit immediately */ - if (purb->status == -ESHUTDOWN) { - DBG("%s, err shutdown\n", __func__); - pipe_info->err_count++; - return; - } - - if (pipe_info->state == 0) { - DBG("exiting USB pipe\n"); - return; - } - - if (purb->actual_length > pipe_info->transfer_size) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return; - } - - if (purb->status == 0) - zr364xx_read_video_callback(cam, pipe_info, purb); - else { - pipe_info->err_count++; - DBG("%s: failed URB %d\n", __func__, purb->status); - } - - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - - /* reuse urb */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - if (pipe_info->state != 0) { - purb->status = usb_submit_urb(pipe_info->stream_urb, - GFP_ATOMIC); - - if (purb->status) - dev_err(&cam->udev->dev, - "error submitting urb (error=%i)\n", - purb->status); - } else - DBG("read pipe complete state 0\n"); -} - -static int zr364xx_start_readpipe(struct zr364xx_camera *cam) -{ - int pipe; - int retval; - struct zr364xx_pipeinfo *pipe_info = cam->pipe; - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); - - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) - return -ENOMEM; - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - DBG("submitting URB %p\n", pipe_info->stream_urb); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - usb_free_urb(pipe_info->stream_urb); - printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); - return retval; - } - - return 0; -} - -static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe_info; - - if (!cam) { - printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); - return; - } - DBG("stop read pipe\n"); - pipe_info = cam->pipe; - if (pipe_info) { - if (pipe_info->state != 0) - pipe_info->state = 0; - - if (pipe_info->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe_info->stream_urb); - usb_free_urb(pipe_info->stream_urb); - pipe_info->stream_urb = NULL; - } - } - return; -} - -/* starts acquisition process */ -static int zr364xx_start_acquire(struct zr364xx_camera *cam) -{ - int j; - - DBG("start acquire\n"); - - cam->last_frame = -1; - cam->cur_frame = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - cam->b_acquire = 1; - return 0; -} - -static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) -{ - cam->b_acquire = 0; - return 0; -} - -static int zr364xx_prepare(struct zr364xx_camera *cam) -{ - int res; - int i, j; - - for (i = 0; init[cam->method][i].size != -1; i++) { - res = send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (res < 0) { - dev_err(&cam->udev->dev, - "error during open sequence: %d\n", i); - return res; - } - } - - cam->skip = 2; - cam->last_frame = -1; - cam->cur_frame = 0; - cam->frame_count = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - v4l2_ctrl_handler_setup(&cam->ctrl_handler); - return 0; -} - -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int res; - - DBG("%s\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->owner && cam->owner != priv) - return -EBUSY; - - res = zr364xx_prepare(cam); - if (res) - return res; - res = videobuf_streamon(&cam->vb_vidq); - if (res == 0) { - zr364xx_start_acquire(cam); - cam->owner = file->private_data; - } - return res; -} - -static int zr364xx_vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - DBG("%s\n", __func__); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cam->owner && cam->owner != priv) - return -EBUSY; - zr364xx_stop_acquire(cam); - return videobuf_streamoff(&cam->vb_vidq); -} - - -/* open the camera */ -static int zr364xx_open(struct file *file) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err; - - DBG("%s\n", __func__); - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = v4l2_fh_open(file); - if (err) - goto out; - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - err = 0; - -out: - mutex_unlock(&cam->lock); - DBG("%s: %d\n", __func__, err); - return err; -} - -static void zr364xx_board_uninit(struct zr364xx_camera *cam) -{ - unsigned long i; - - zr364xx_stop_readpipe(cam); - - /* release sys buffers */ - for (i = 0; i < FRAMES; i++) { - if (cam->buffer.frame[i].lpvbits) { - DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); - vfree(cam->buffer.frame[i].lpvbits); - } - cam->buffer.frame[i].lpvbits = NULL; - } - - /* release transfer buffer */ - kfree(cam->pipe->transfer_buffer); -} - -static void zr364xx_release(struct v4l2_device *v4l2_dev) -{ - struct zr364xx_camera *cam = - container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); - - videobuf_mmap_free(&cam->vb_vidq); - v4l2_ctrl_handler_free(&cam->ctrl_handler); - zr364xx_board_uninit(cam); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); -} - -/* release the camera */ -static int zr364xx_close(struct file *file) -{ - struct zr364xx_camera *cam; - struct usb_device *udev; - int i; - - DBG("%s\n", __func__); - cam = video_drvdata(file); - - mutex_lock(&cam->lock); - udev = cam->udev; - - if (file->private_data == cam->owner) { - /* turn off stream */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - videobuf_streamoff(&cam->vb_vidq); - - for (i = 0; i < 2; i++) { - send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - } - cam->owner = NULL; - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - mutex_unlock(&cam->lock); - return v4l2_fh_release(file); -} - - -static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int ret; - - if (!cam) { - DBG("%s: cam == NULL\n", __func__); - return -ENODEV; - } - DBG("mmap called, vma=%p\n", vma); - - ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); - - DBG("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; -} - -static __poll_t zr364xx_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - __poll_t res = v4l2_ctrl_poll(file, wait); - - _DBG("%s\n", __func__); - - return res | videobuf_poll_stream(file, q, wait); -} - -static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { - .s_ctrl = zr364xx_s_ctrl, -}; - -static const struct v4l2_file_operations zr364xx_fops = { - .owner = THIS_MODULE, - .open = zr364xx_open, - .release = zr364xx_close, - .read = zr364xx_read, - .mmap = zr364xx_mmap, - .unlocked_ioctl = video_ioctl2, - .poll = zr364xx_poll, -}; - -static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { - .vidioc_querycap = zr364xx_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = zr364xx_vidioc_enum_input, - .vidioc_g_input = zr364xx_vidioc_g_input, - .vidioc_s_input = zr364xx_vidioc_s_input, - .vidioc_streamon = zr364xx_vidioc_streamon, - .vidioc_streamoff = zr364xx_vidioc_streamoff, - .vidioc_reqbufs = zr364xx_vidioc_reqbufs, - .vidioc_querybuf = zr364xx_vidioc_querybuf, - .vidioc_qbuf = zr364xx_vidioc_qbuf, - .vidioc_dqbuf = zr364xx_vidioc_dqbuf, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device zr364xx_template = { - .name = DRIVER_DESC, - .fops = &zr364xx_fops, - .ioctl_ops = &zr364xx_ioctl_ops, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, -}; - - - -/*******************/ -/* USB integration */ -/*******************/ -static int zr364xx_board_init(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe = cam->pipe; - unsigned long i; - int err; - - DBG("board init: %p\n", cam); - memset(pipe, 0, sizeof(*pipe)); - pipe->cam = cam; - pipe->transfer_size = BUFFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->transfer_size, - GFP_KERNEL); - if (!pipe->transfer_buffer) { - DBG("out of memory!\n"); - return -ENOMEM; - } - - cam->b_acquire = 0; - cam->frame_count = 0; - - /*** start create system buffers ***/ - for (i = 0; i < FRAMES; i++) { - /* always allocate maximum size for system buffers */ - cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); - - DBG("valloc %p, idx %lu, pdata %p\n", - &cam->buffer.frame[i], i, - cam->buffer.frame[i].lpvbits); - if (!cam->buffer.frame[i].lpvbits) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. Using less frames\n"); - break; - } - } - - if (i == 0) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); - err = -ENOMEM; - goto err_free; - } else - cam->buffer.dwFrames = i; - - /* make sure internal states are set */ - for (i = 0; i < FRAMES; i++) { - cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[i].cur_size = 0; - } - - cam->cur_frame = 0; - cam->last_frame = -1; - /*** end create system buffers ***/ - - /* start read pipe */ - err = zr364xx_start_readpipe(cam); - if (err) - goto err_free_frames; - - DBG(": board initialized\n"); - return 0; - -err_free_frames: - for (i = 0; i < FRAMES; i++) - vfree(cam->buffer.frame[i].lpvbits); -err_free: - kfree(cam->pipe->transfer_buffer); - cam->pipe->transfer_buffer = NULL; - return err; -} - -static int zr364xx_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct zr364xx_camera *cam = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct v4l2_ctrl_handler *hdl; - int err; - int i; - - DBG("probing...\n"); - - dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); - dev_info(&intf->dev, "model %04x:%04x detected\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); - if (err < 0) { - dev_err(&udev->dev, "couldn't register v4l2_device\n"); - goto free_cam; - } - hdl = &cam->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 1); - v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); - if (hdl->error) { - err = hdl->error; - dev_err(&udev->dev, "couldn't register control\n"); - goto free_hdlr_and_unreg_dev; - } - /* save the init method used by this camera */ - cam->method = id->driver_info; - mutex_init(&cam->lock); - cam->vdev = zr364xx_template; - cam->vdev.lock = &cam->lock; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.ctrl_handler = &cam->ctrl_handler; - video_set_drvdata(&cam->vdev, cam); - - cam->udev = udev; - - switch (mode) { - case 1: - dev_info(&udev->dev, "160x120 mode selected\n"); - cam->width = 160; - cam->height = 120; - break; - case 2: - dev_info(&udev->dev, "640x480 mode selected\n"); - cam->width = 640; - cam->height = 480; - break; - default: - dev_info(&udev->dev, "320x240 mode selected\n"); - cam->width = 320; - cam->height = 240; - break; - } - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - cam->nb = 0; - - DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); - - /* set up the endpoint information */ - iface_desc = intf->cur_altsetting; - DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - cam->read_endpoint = endpoint->bEndpointAddress; - } - } - - if (!cam->read_endpoint) { - err = -ENOMEM; - dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); - goto free_hdlr_and_unreg_dev; - } - - /* v4l */ - INIT_LIST_HEAD(&cam->vidq.active); - cam->vidq.cam = cam; - - usb_set_intfdata(intf, cam); - - /* load zr364xx board specific */ - err = zr364xx_board_init(cam); - if (err) - goto free_hdlr_and_unreg_dev; - err = v4l2_ctrl_handler_setup(hdl); - if (err) - goto board_uninit; - - spin_lock_init(&cam->slock); - - cam->fmt = formats; - - videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, - NULL, &cam->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct zr364xx_buffer), cam, &cam->lock); - - err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); - if (err) { - dev_err(&udev->dev, "video_register_device failed\n"); - goto board_uninit; - } - cam->v4l2_dev.release = zr364xx_release; - - dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", - video_device_node_name(&cam->vdev)); - return 0; - -board_uninit: - zr364xx_board_uninit(cam); -free_hdlr_and_unreg_dev: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&cam->v4l2_dev); -free_cam: - kfree(cam); - return err; -} - - -static void zr364xx_disconnect(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->lock); - usb_set_intfdata(intf, NULL); - dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - video_unregister_device(&cam->vdev); - v4l2_device_disconnect(&cam->v4l2_dev); - - /* stops the read pipe if it is running */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - - zr364xx_stop_readpipe(cam); - mutex_unlock(&cam->lock); - v4l2_device_put(&cam->v4l2_dev); -} - - -#ifdef CONFIG_PM -static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - cam->was_streaming = cam->b_acquire; - if (!cam->was_streaming) - return 0; - zr364xx_stop_acquire(cam); - zr364xx_stop_readpipe(cam); - return 0; -} - -static int zr364xx_resume(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - int res; - - if (!cam->was_streaming) - return 0; - - res = zr364xx_start_readpipe(cam); - if (res) - return res; - - res = zr364xx_prepare(cam); - if (res) - goto err_prepare; - - zr364xx_start_acquire(cam); - return 0; - -err_prepare: - zr364xx_stop_readpipe(cam); - return res; -} -#endif - -/**********************/ -/* Module integration */ -/**********************/ - -static struct usb_driver zr364xx_driver = { - .name = "zr364xx", - .probe = zr364xx_probe, - .disconnect = zr364xx_disconnect, -#ifdef CONFIG_PM - .suspend = zr364xx_suspend, - .resume = zr364xx_resume, - .reset_resume = zr364xx_resume, -#endif - .id_table = device_table -}; - -module_usb_driver(zr364xx_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index ed3e484603d7..84f627ccf63e 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -58,6 +58,7 @@ if STAGING_MEDIA_DEPRECATED source "drivers/staging/media/deprecated/cpia2/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" +source "drivers/staging/media/deprecated/zr364xx/Kconfig" endif endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 822c70ab4c0b..ed7e5a61fc00 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ obj-$(CONFIG_DVB_AV7110) += av7110/ diff --git a/drivers/staging/media/deprecated/zr364xx/Kconfig b/drivers/staging/media/deprecated/zr364xx/Kconfig new file mode 100644 index 000000000000..ea29c9d8dca2 --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_ZR364XX + tristate "USB ZR364XX Camera support (DEPRECATED)" + depends on USB && VIDEO_DEV + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC + help + Say Y here if you want to connect this type of camera to your + computer's USB port. + See for more info + and list of supported cameras. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + diff --git a/drivers/staging/media/deprecated/zr364xx/Makefile b/drivers/staging/media/deprecated/zr364xx/Makefile new file mode 100644 index 000000000000..edab017d499c --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o + diff --git a/drivers/staging/media/deprecated/zr364xx/TODO b/drivers/staging/media/deprecated/zr364xx/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/zr364xx/zr364xx.c b/drivers/staging/media/deprecated/zr364xx/zr364xx.c new file mode 100644 index 000000000000..538a330046ec --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/zr364xx.c @@ -0,0 +1,1635 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran 364xx based USB webcam module version 0.73 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy development ! + * + * Copyright (C) 2004 Antoine Jacquet + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Version Information */ +#define DRIVER_VERSION "0.7.4" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 + +/* Debug macro */ +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 +#define METHOD3 3 + + +/* Module parameters */ +static int debug; +static int mode; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, + {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* v4l video device */ + struct v4l2_fh *owner; /* owns the streaming */ + int nb; + struct zr364xx_bufferi buffer; + int skip; + int width; + int height; + int method; + struct mutex lock; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + bool was_streaming; +}; + +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmemdup(cp, size, GFP_KERNEL); + if (!transfer_buffer) + return -ENOMEM; + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[4] = { m0, m1, m2, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; + + *size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (*count == 0) + *count = ZR364XX_DEF_BUFS; + + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d\n", __func__, field); + if (!cam->fmt) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + _DBG("%s\n", __func__); + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); + +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, + loff_t * ppos) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err = 0; + + _DBG("%s\n", __func__); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = zr364xx_vidioc_streamon(file, file->private_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (err == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, + (int) count, (int) *ppos); + + /* NoMan Sux ! */ + err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + mutex_unlock(&cam->lock); + return err; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer %p size= %d\n", __func__, vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + buf->vb.ts = ktime_get_ns(); + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + 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); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return rc; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = cam->cur_frame; + struct zr364xx_framei *frm = &cam->buffer.frame[idx]; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + if (purb->actual_length < 128) { + /* header incomplete */ + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n", + __func__, purb->actual_length); + return -EINVAL; + } + + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + strscpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + if (cam->udev->product) + strscpy(cap->card, cam->udev->product, sizeof(cap->card)); + strscpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strscpy(i->name, DRIVER_DESC " Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct zr364xx_camera *cam = + container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* hardware brightness */ + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - ctrl->val; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + f->pixelformat = formats[0].fourcc; + return 0; +} + +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + +static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; + + if (!cam) + return -ENODEV; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); + return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return 0; +} + +static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam; + + if (!file) + return -ENODEV; + cam = video_drvdata(file); + + f->fmt.pix.pixelformat = formats[0].fourcc; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + return 0; +} + +static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; + + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (cam->owner) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + DBG("%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + cam->vb_vidq.field = f->fmt.pix.field; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + if (cam->owner && cam->owner != priv) + return -EBUSY; + return videobuf_reqbufs(&cam->vb_vidq, p); +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (!pipe_info) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) + return -ENOMEM; + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + usb_free_urb(pipe_info->stream_urb); + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; + return 0; +} + +static int zr364xx_prepare(struct zr364xx_camera *cam) +{ + int res; + int i, j; + + for (i = 0; init[cam->method][i].size != -1; i++) { + res = send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (res < 0) { + dev_err(&cam->udev->dev, + "error during open sequence: %d\n", i); + return res; + } + } + + cam->skip = 2; + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + v4l2_ctrl_handler_setup(&cam->ctrl_handler); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int res; + + DBG("%s\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + + res = zr364xx_prepare(cam); + if (res) + return res; + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + cam->owner = file->private_data; + } + return res; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cam->owner && cam->owner != priv) + return -EBUSY; + zr364xx_stop_acquire(cam); + return videobuf_streamoff(&cam->vb_vidq); +} + + +/* open the camera */ +static int zr364xx_open(struct file *file) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err; + + DBG("%s\n", __func__); + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = v4l2_fh_open(file); + if (err) + goto out; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + err = 0; + +out: + mutex_unlock(&cam->lock); + DBG("%s: %d\n", __func__, err); + return err; +} + +static void zr364xx_board_uninit(struct zr364xx_camera *cam) +{ + unsigned long i; + + zr364xx_stop_readpipe(cam); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); +} + +static void zr364xx_release(struct v4l2_device *v4l2_dev) +{ + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); + + videobuf_mmap_free(&cam->vb_vidq); + v4l2_ctrl_handler_free(&cam->ctrl_handler); + zr364xx_board_uninit(cam); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +/* release the camera */ +static int zr364xx_close(struct file *file) +{ + struct zr364xx_camera *cam; + struct usb_device *udev; + int i; + + DBG("%s\n", __func__); + cam = video_drvdata(file); + + mutex_lock(&cam->lock); + udev = cam->udev; + + if (file->private_data == cam->owner) { + /* turn off stream */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + + for (i = 0; i < 2; i++) { + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + } + cam->owner = NULL; + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + mutex_unlock(&cam->lock); + return v4l2_fh_release(file); +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int ret; + + if (!cam) { + DBG("%s: cam == NULL\n", __func__); + return -ENODEV; + } + DBG("mmap called, vma=%p\n", vma); + + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static __poll_t zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + __poll_t res = v4l2_ctrl_poll(file, wait); + + _DBG("%s\n", __func__); + + return res | videobuf_poll_stream(file, q, wait); +} + +static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { + .s_ctrl = zr364xx_s_ctrl, +}; + +static const struct v4l2_file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_close, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = zr364xx_poll, +}; + +static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device zr364xx_template = { + .name = DRIVER_DESC, + .fops = &zr364xx_fops, + .ioctl_ops = &zr364xx_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + int err; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (!pipe->transfer_buffer) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (!cam->buffer.frame[i].lpvbits) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + err = -ENOMEM; + goto err_free; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + err = zr364xx_start_readpipe(cam); + if (err) + goto err_free_frames; + + DBG(": board initialized\n"); + return 0; + +err_free_frames: + for (i = 0; i < FRAMES; i++) + vfree(cam->buffer.frame[i].lpvbits); +err_free: + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return err; +} + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct v4l2_ctrl_handler *hdl; + int err; + int i; + + DBG("probing...\n"); + + dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); + dev_info(&intf->dev, "model %04x:%04x detected\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + goto free_cam; + } + hdl = &cam->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto free_hdlr_and_unreg_dev; + } + /* save the init method used by this camera */ + cam->method = id->driver_info; + mutex_init(&cam->lock); + cam->vdev = zr364xx_template; + cam->vdev.lock = &cam->lock; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.ctrl_handler = &cam->ctrl_handler; + video_set_drvdata(&cam->vdev, cam); + + cam->udev = udev; + + switch (mode) { + case 1: + dev_info(&udev->dev, "160x120 mode selected\n"); + cam->width = 160; + cam->height = 120; + break; + case 2: + dev_info(&udev->dev, "640x480 mode selected\n"); + cam->width = 640; + cam->height = 480; + break; + default: + dev_info(&udev->dev, "320x240 mode selected\n"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + err = -ENOMEM; + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + goto free_hdlr_and_unreg_dev; + } + + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; + + usb_set_intfdata(intf, cam); + + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (err) + goto free_hdlr_and_unreg_dev; + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto board_uninit; + + spin_lock_init(&cam->slock); + + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam, &cam->lock); + + err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); + if (err) { + dev_err(&udev->dev, "video_register_device failed\n"); + goto board_uninit; + } + cam->v4l2_dev.release = zr364xx_release; + + dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", + video_device_node_name(&cam->vdev)); + return 0; + +board_uninit: + zr364xx_board_uninit(cam); +free_hdlr_and_unreg_dev: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&cam->v4l2_dev); +free_cam: + kfree(cam); + return err; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->lock); + usb_set_intfdata(intf, NULL); + dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); + video_unregister_device(&cam->vdev); + v4l2_device_disconnect(&cam->v4l2_dev); + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + mutex_unlock(&cam->lock); + v4l2_device_put(&cam->v4l2_dev); +} + + +#ifdef CONFIG_PM +static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + cam->was_streaming = cam->b_acquire; + if (!cam->was_streaming) + return 0; + zr364xx_stop_acquire(cam); + zr364xx_stop_readpipe(cam); + return 0; +} + +static int zr364xx_resume(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + int res; + + if (!cam->was_streaming) + return 0; + + res = zr364xx_start_readpipe(cam); + if (res) + return res; + + res = zr364xx_prepare(cam); + if (res) + goto err_prepare; + + zr364xx_start_acquire(cam); + return 0; + +err_prepare: + zr364xx_stop_readpipe(cam); + return res; +} +#endif + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, +#ifdef CONFIG_PM + .suspend = zr364xx_suspend, + .resume = zr364xx_resume, + .reset_resume = zr364xx_resume, +#endif + .id_table = device_table +}; + +module_usb_driver(zr364xx_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); -- cgit v1.3-14-g43fede From b7eeabc1cee3c031123bb4a3a7786779e1a57ac2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:45 +0200 Subject: media: tm6000: deprecate this driver Deprecate the tm6000 driver. This driver does not use the vb2 framework for video streaming, instead it uses the old videobuf framework. We want to get rid of these old drivers, so deprecated it for future removal. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/usb/Kconfig | 1 - drivers/media/usb/Makefile | 1 - drivers/media/usb/tm6000/Kconfig | 34 - drivers/media/usb/tm6000/Makefile | 14 - drivers/media/usb/tm6000/tm6000-alsa.c | 440 ----- drivers/media/usb/tm6000/tm6000-cards.c | 1397 ---------------- drivers/media/usb/tm6000/tm6000-core.c | 916 ----------- drivers/media/usb/tm6000/tm6000-dvb.c | 454 ------ drivers/media/usb/tm6000/tm6000-i2c.c | 317 ---- drivers/media/usb/tm6000/tm6000-input.c | 503 ------ drivers/media/usb/tm6000/tm6000-regs.h | 588 ------- drivers/media/usb/tm6000/tm6000-stds.c | 623 ------- drivers/media/usb/tm6000/tm6000-usb-isoc.h | 38 - drivers/media/usb/tm6000/tm6000-video.c | 1705 -------------------- drivers/media/usb/tm6000/tm6000.h | 396 ----- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/deprecated/tm6000/Kconfig | 37 + drivers/staging/media/deprecated/tm6000/Makefile | 14 + drivers/staging/media/deprecated/tm6000/TODO | 7 + .../staging/media/deprecated/tm6000/tm6000-alsa.c | 440 +++++ .../staging/media/deprecated/tm6000/tm6000-cards.c | 1397 ++++++++++++++++ .../staging/media/deprecated/tm6000/tm6000-core.c | 916 +++++++++++ .../staging/media/deprecated/tm6000/tm6000-dvb.c | 454 ++++++ .../staging/media/deprecated/tm6000/tm6000-i2c.c | 317 ++++ .../staging/media/deprecated/tm6000/tm6000-input.c | 503 ++++++ .../staging/media/deprecated/tm6000/tm6000-regs.h | 588 +++++++ .../staging/media/deprecated/tm6000/tm6000-stds.c | 623 +++++++ .../media/deprecated/tm6000/tm6000-usb-isoc.h | 38 + .../staging/media/deprecated/tm6000/tm6000-video.c | 1705 ++++++++++++++++++++ drivers/staging/media/deprecated/tm6000/tm6000.h | 396 +++++ 32 files changed, 7438 insertions(+), 7428 deletions(-) delete mode 100644 drivers/media/usb/tm6000/Kconfig delete mode 100644 drivers/media/usb/tm6000/Makefile delete mode 100644 drivers/media/usb/tm6000/tm6000-alsa.c delete mode 100644 drivers/media/usb/tm6000/tm6000-cards.c delete mode 100644 drivers/media/usb/tm6000/tm6000-core.c delete mode 100644 drivers/media/usb/tm6000/tm6000-dvb.c delete mode 100644 drivers/media/usb/tm6000/tm6000-i2c.c delete mode 100644 drivers/media/usb/tm6000/tm6000-input.c delete mode 100644 drivers/media/usb/tm6000/tm6000-regs.h delete mode 100644 drivers/media/usb/tm6000/tm6000-stds.c delete mode 100644 drivers/media/usb/tm6000/tm6000-usb-isoc.h delete mode 100644 drivers/media/usb/tm6000/tm6000-video.c delete mode 100644 drivers/media/usb/tm6000/tm6000.h create mode 100644 drivers/staging/media/deprecated/tm6000/Kconfig create mode 100644 drivers/staging/media/deprecated/tm6000/Makefile create mode 100644 drivers/staging/media/deprecated/tm6000/TODO create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-alsa.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-cards.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-core.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-dvb.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-i2c.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-input.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-regs.h create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-stds.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000-video.c create mode 100644 drivers/staging/media/deprecated/tm6000/tm6000.h diff --git a/MAINTAINERS b/MAINTAINERS index e26da9b8e277..2a61d11ed3e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20469,7 +20469,7 @@ S: Odd fixes W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git F: Documentation/admin-guide/media/tm6000* -F: drivers/media/usb/tm6000/ +F: drivers/staging/media/deprecated/tm6000/ TMIO/SDHI MMC DRIVER M: Wolfram Sang diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 3d0138f8573c..813171d25ac5 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -36,7 +36,6 @@ if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" -source "drivers/media/usb/tm6000/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 7fccc6604b1f..6d171beea20d 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -29,5 +29,4 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_STK1160) += stk1160/ -obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ diff --git a/drivers/media/usb/tm6000/Kconfig b/drivers/media/usb/tm6000/Kconfig deleted file mode 100644 index 56e977deba81..000000000000 --- a/drivers/media/usb/tm6000/Kconfig +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_TM6000 - tristate "TV Master TM5600/6000/6010 driver" - depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB - select VIDEO_TUNER - select MEDIA_TUNER_XC2028 - select MEDIA_TUNER_XC5000 - select VIDEOBUF_VMALLOC - help - Support for TM5600/TM6000/TM6010 USB Device - - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the usb bus, so you need - an external software decoder to watch TV on your computer. - - Say Y if you own such a device and want to use it. - -config VIDEO_TM6000_ALSA - tristate "TV Master TM5600/6000/6010 audio support" - depends on VIDEO_TM6000 && SND - select SND_PCM - help - This is a video4linux driver for direct (DMA) audio for - TM5600/TM6000/TM6010 USB Devices. - - To compile this driver as a module, choose M here: the - module will be called tm6000-alsa. - -config VIDEO_TM6000_DVB - tristate "DVB Support for tm6000 based TV cards" - depends on VIDEO_TM6000 && DVB_CORE && USB - select DVB_ZL10353 - help - This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile deleted file mode 100644 index 75247a02a485..000000000000 --- a/drivers/media/usb/tm6000/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -tm6000-y := tm6000-cards.o \ - tm6000-core.o \ - tm6000-i2c.o \ - tm6000-video.o \ - tm6000-stds.o \ - tm6000-input.o - -obj-$(CONFIG_VIDEO_TM6000) += tm6000.o -obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o -obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o - -ccflags-y += -I $(srctree)/drivers/media/tuners -ccflags-y += -I $(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c deleted file mode 100644 index a19a46770c2b..000000000000 --- a/drivers/media/usb/tm6000/tm6000-alsa.c +++ /dev/null @@ -1,440 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Support for audio capture for tm5600/6000/6010 -// Copyright (c) 2007-2008 Mauro Carvalho Chehab -// -// Based on cx88-alsa.c - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -#include "tm6000.h" -#include "tm6000-regs.h" - -#undef dprintk - -#define dprintk(level, fmt, arg...) do { \ - if (debug >= level) \ - printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ - } while (0) - -/**************************************************************************** - Module global static vars - ****************************************************************************/ - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ - -static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; - -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); - - -/**************************************************************************** - Module macros - ****************************************************************************/ - -MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL v2"); -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug messages"); - -/**************************************************************************** - Module specific functions - ****************************************************************************/ - -/* - * BOARD Specific: Sets audio DMA - */ - -static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) -{ - struct tm6000_core *core = chip->core; - - dprintk(1, "Starting audio DMA\n"); - - /* Enables audio */ - tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); - - tm6000_set_audio_bitrate(core, 48000); - - return 0; -} - -/* - * BOARD Specific: Resets audio DMA - */ -static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) -{ - struct tm6000_core *core = chip->core; - - dprintk(1, "Stopping audio DMA\n"); - - /* Disables audio */ - tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); - - return 0; -} - -/**************************************************************************** - ALSA PCM Interface - ****************************************************************************/ - -/* - * Digital hardware definition - */ -#define DEFAULT_FIFO_SIZE 4096 - -static const struct snd_pcm_hardware snd_tm6000_digital_hw = { - .info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .period_bytes_min = 64, - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98, - .buffer_bytes_max = 62720 * 8, -}; - -/* - * audio pcm capture open callback - */ -static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - err = snd_pcm_hw_constraint_pow2(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIODS); - if (err < 0) - goto _error; - - chip->substream = substream; - - runtime->hw = snd_tm6000_digital_hw; - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - - return 0; -_error: - dprintk(1, "Error opening PCM!\n"); - return err; -} - -/* - * audio close callback - */ -static int snd_tm6000_close(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct tm6000_core *core = chip->core; - - if (atomic_read(&core->stream_started) > 0) { - atomic_set(&core->stream_started, 0); - schedule_work(&core->wq_trigger); - } - - return 0; -} - -static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) -{ - struct snd_tm6000_card *chip = core->adev; - struct snd_pcm_substream *substream = chip->substream; - struct snd_pcm_runtime *runtime; - int period_elapsed = 0; - unsigned int stride, buf_pos; - int length; - - if (atomic_read(&core->stream_started) == 0) - return 0; - - if (!size || !substream) { - dprintk(1, "substream was NULL\n"); - return -EINVAL; - } - - runtime = substream->runtime; - if (!runtime || !runtime->dma_area) { - dprintk(1, "runtime was NULL\n"); - return -EINVAL; - } - - buf_pos = chip->buf_pos; - stride = runtime->frame_bits >> 3; - - if (stride == 0) { - dprintk(1, "stride is zero\n"); - return -EINVAL; - } - - length = size / stride; - if (length == 0) { - dprintk(1, "%s: length was zero\n", __func__); - return -EINVAL; - } - - dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, - runtime->dma_area, buf_pos, - (unsigned int)runtime->buffer_size, stride); - - if (buf_pos + length >= runtime->buffer_size) { - unsigned int cnt = runtime->buffer_size - buf_pos; - memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); - memcpy(runtime->dma_area, buf + cnt * stride, - length * stride - cnt * stride); - } else - memcpy(runtime->dma_area + buf_pos * stride, buf, - length * stride); - - snd_pcm_stream_lock(substream); - - chip->buf_pos += length; - if (chip->buf_pos >= runtime->buffer_size) - chip->buf_pos -= runtime->buffer_size; - - chip->period_pos += length; - if (chip->period_pos >= runtime->period_size) { - chip->period_pos -= runtime->period_size; - period_elapsed = 1; - } - - snd_pcm_stream_unlock(substream); - - if (period_elapsed) - snd_pcm_period_elapsed(substream); - - return 0; -} - -/* - * prepare callback - */ -static int snd_tm6000_prepare(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - chip->buf_pos = 0; - chip->period_pos = 0; - - return 0; -} - - -/* - * trigger callback - */ -static void audio_trigger(struct work_struct *work) -{ - struct tm6000_core *core = container_of(work, struct tm6000_core, - wq_trigger); - struct snd_tm6000_card *chip = core->adev; - - if (atomic_read(&core->stream_started)) { - dprintk(1, "starting capture"); - _tm6000_start_audio_dma(chip); - } else { - dprintk(1, "stopping capture"); - _tm6000_stop_audio_dma(chip); - } -} - -static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct tm6000_core *core = chip->core; - int err = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_START: - atomic_set(&core->stream_started, 1); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - atomic_set(&core->stream_started, 0); - break; - default: - err = -EINVAL; - break; - } - schedule_work(&core->wq_trigger); - - return err; -} -/* - * pointer callback - */ -static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) -{ - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - return chip->buf_pos; -} - -/* - * operators - */ -static const struct snd_pcm_ops snd_tm6000_pcm_ops = { - .open = snd_tm6000_pcm_open, - .close = snd_tm6000_close, - .prepare = snd_tm6000_prepare, - .trigger = snd_tm6000_card_trigger, - .pointer = snd_tm6000_pointer, -}; - -/* - * create a PCM device - */ - -/* FIXME: Control interface - How to control volume/mute? */ - -/**************************************************************************** - Basic Flow for Sound Devices - ****************************************************************************/ - -/* - * Alsa Constructor - Component probe - */ -static int tm6000_audio_init(struct tm6000_core *dev) -{ - struct snd_card *card; - struct snd_tm6000_card *chip; - int rc; - static int devnr; - char component[14]; - struct snd_pcm *pcm; - - if (!dev) - return 0; - - if (devnr >= SNDRV_CARDS) - return -ENODEV; - - if (!enable[devnr]) - return -ENOENT; - - rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000", - THIS_MODULE, 0, &card); - if (rc < 0) { - snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); - return rc; - } - strscpy(card->driver, "tm6000-alsa", sizeof(card->driver)); - strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname)); - sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", - dev->udev->bus->busnum, dev->udev->devnum); - - sprintf(component, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - snd_component_add(card, component); - - chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); - if (!chip) { - rc = -ENOMEM; - goto error; - } - - chip->core = dev; - chip->card = card; - dev->adev = chip; - spin_lock_init(&chip->reg_lock); - - rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); - if (rc < 0) - goto error_chip; - - pcm->info_flags = 0; - pcm->private_data = chip; - strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name)); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); - snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); - - INIT_WORK(&dev->wq_trigger, audio_trigger); - rc = snd_card_register(card); - if (rc < 0) - goto error_chip; - - dprintk(1, "Registered audio driver for %s\n", card->longname); - - return 0; - -error_chip: - kfree(chip); - dev->adev = NULL; -error: - snd_card_free(card); - return rc; -} - -static int tm6000_audio_fini(struct tm6000_core *dev) -{ - struct snd_tm6000_card *chip; - - if (!dev) - return 0; - chip = dev->adev; - - if (!chip) - return 0; - - if (!chip->card) - return 0; - - snd_card_free(chip->card); - chip->card = NULL; - kfree(chip); - dev->adev = NULL; - - return 0; -} - -static struct tm6000_ops audio_ops = { - .type = TM6000_AUDIO, - .name = "TM6000 Audio Extension", - .init = tm6000_audio_init, - .fini = tm6000_audio_fini, - .fillbuf = tm6000_fillbuf, -}; - -static int __init tm6000_alsa_register(void) -{ - return tm6000_register_extension(&audio_ops); -} - -static void __exit tm6000_alsa_unregister(void) -{ - tm6000_unregister_extension(&audio_ops); -} - -module_init(tm6000_alsa_register); -module_exit(tm6000_alsa_unregister); diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c deleted file mode 100644 index b7842cd6f9af..000000000000 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ /dev/null @@ -1,1397 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices -// -// Copyright (c) 2006-2007 Mauro Carvalho Chehab - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tm6000.h" -#include "tm6000-regs.h" -#include "xc2028.h" -#include "xc5000.h" - -#define TM6000_BOARD_UNKNOWN 0 -#define TM5600_BOARD_GENERIC 1 -#define TM6000_BOARD_GENERIC 2 -#define TM6010_BOARD_GENERIC 3 -#define TM5600_BOARD_10MOONS_UT821 4 -#define TM5600_BOARD_10MOONS_UT330 5 -#define TM6000_BOARD_ADSTECH_DUAL_TV 6 -#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 -#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 -#define TM6010_BOARD_HAUPPAUGE_900H 9 -#define TM6010_BOARD_BEHOLD_WANDER 10 -#define TM6010_BOARD_BEHOLD_VOYAGER 11 -#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 -#define TM6010_BOARD_TWINHAN_TU501 13 -#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 -#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 -#define TM5600_BOARD_TERRATEC_GRABSTER 16 - -#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ - (model == TM5600_BOARD_GENERIC) || \ - (model == TM6000_BOARD_GENERIC) || \ - (model == TM6010_BOARD_GENERIC)) - -#define TM6000_MAXBOARDS 16 -static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; - -module_param_array(card, int, NULL, 0444); - -static unsigned long tm6000_devused; - - -struct tm6000_board { - char *name; - char eename[16]; /* EEPROM name */ - unsigned eename_size; /* size of EEPROM name */ - unsigned eename_pos; /* Position where it appears at ROM */ - - struct tm6000_capabilities caps; - - enum tm6000_devtype type; /* variant of the chipset */ - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - int demod_addr; /* demodulator address */ - - struct tm6000_gpio gpio; - - struct tm6000_input vinput[3]; - struct tm6000_input rinput; - - char *ir_codes; -}; - -static struct tm6000_board tm6000_boards[] = { - [TM6000_BOARD_UNKNOWN] = { - .name = "Unknown tm6000 video grabber", - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_GENERIC] = { - .name = "Generic tm5600 board", - .type = TM5600, - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_GENERIC] = { - .name = "Generic tm6000 board", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_GENERIC] = { - .name = "Generic tm6010 board", - .type = TM6010, - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_10MOONS_UT821] = { - .name = "10Moons UT 821", - .tuner_type = TUNER_XC2028, - .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, - .eename_size = 14, - .eename_pos = 0x14, - .type = TM5600, - .tuner_addr = 0xc2 >> 1, - .caps = { - .has_tuner = 1, - .has_eeprom = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM5600_BOARD_10MOONS_UT330] = { - .name = "10Moons UT 330", - .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, - .tuner_addr = 0xc8 >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_ADSTECH_DUAL_TV] = { - .name = "ADSTECH Dual TV USB", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0xc8 >> 1, - .caps = { - .has_tuner = 1, - .has_tda9874 = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_FREECOM_AND_SIMILAR] = { - .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 0, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_4, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { - .name = "ADSTECH Mini Dual TV USB", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc8 >> 1, - .demod_addr = 0x1e >> 1, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 0, - }, - .gpio = { - .tuner_reset = TM6000_GPIO_4, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_HAUPPAUGE_900H] = { - .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", - .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, - .eename_size = 14, - .eename_pos = 0x42, - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .ir_codes = RC_MAP_HAUPPAUGE, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_BEHOLD_WANDER] = { - .name = "Beholder Wander DVB-T/TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .demod_reset = TM6010_GPIO_1, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_BEHOLD_VOYAGER] = { - .name = "Beholder Voyager TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { - .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_SIF1, - }, - }, - [TM5600_BOARD_TERRATEC_GRABSTER] = { - .name = "Terratec Grabster AV 150/250 MX", - .type = TM5600, - .tuner_type = TUNER_ABSENT, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_ADC1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_TWINHAN_TU501] = { - .name = "Twinhan TU501(704D1)", - .tuner_type = TUNER_XC2028, /* has a XC3028 */ - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_2, - .tuner_on = TM6010_GPIO_3, - .demod_reset = TM6010_GPIO_1, - .demod_on = TM6010_GPIO_4, - .power_led = TM6010_GPIO_7, - .dvb_led = TM6010_GPIO_5, - .ir = TM6010_GPIO_0, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, { - .type = TM6000_INPUT_COMPOSITE1, - .vmux = TM6000_VMUX_VIDEO_A, - .amux = TM6000_AMUX_ADC2, - }, { - .type = TM6000_INPUT_SVIDEO, - .vmux = TM6000_VMUX_VIDEO_AB, - .amux = TM6000_AMUX_ADC2, - }, - }, - }, - [TM6010_BOARD_BEHOLD_WANDER_LITE] = { - .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .demod_addr = 0x1e >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 1, - .has_zl10353 = 1, - .has_eeprom = 1, - .has_remote = 0, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .demod_reset = TM6010_GPIO_1, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, - [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { - .name = "Beholder Voyager Lite TV/FM USB2.0", - .tuner_type = TUNER_XC5000, - .tuner_addr = 0xc2 >> 1, - .type = TM6010, - .caps = { - .has_tuner = 1, - .has_dvb = 0, - .has_zl10353 = 0, - .has_eeprom = 1, - .has_remote = 0, - .has_radio = 1, - }, - .gpio = { - .tuner_reset = TM6010_GPIO_0, - .power_led = TM6010_GPIO_6, - }, - .vinput = { { - .type = TM6000_INPUT_TV, - .vmux = TM6000_VMUX_VIDEO_B, - .amux = TM6000_AMUX_SIF1, - }, - }, - .rinput = { - .type = TM6000_INPUT_RADIO, - .amux = TM6000_AMUX_ADC1, - }, - }, -}; - -/* table of devices that work with this driver */ -static const struct usb_device_id tm6000_id_table[] = { - { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, - { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, - { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, - { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, - { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, - { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, - { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, - { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, - { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, - { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, - { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, - { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, - { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, - { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, - { } -}; -MODULE_DEVICE_TABLE(usb, tm6000_id_table); - -/* Control power led for show some activity */ -void tm6000_flash_led(struct tm6000_core *dev, u8 state) -{ - /* Power LED unconfigured */ - if (!dev->gpio.power_led) - return; - - /* ON Power LED */ - if (state) { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - break; - } - } - /* OFF Power LED */ - else { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - break; - } - } -} - -/* Tuner callback to provide the proper gpio changes needed for xc5000 */ -int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct tm6000_core *dev = ptr; - - if (dev->tuner_type != TUNER_XC5000) - return 0; - - switch (command) { - case XC5000_TUNER_RESET: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(15); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(15); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - break; - } - return rc; -} -EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); - -/* Tuner callback to provide the proper gpio changes needed for xc2028 */ - -int tm6000_tuner_callback(void *ptr, int component, int command, int arg) -{ - int rc = 0; - struct tm6000_core *dev = ptr; - - if (dev->tuner_type != TUNER_XC2028) - return 0; - - switch (command) { - case XC2028_RESET_CLK: - tm6000_ir_wait(dev, 0); - - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, - 0x02, arg); - msleep(10); - rc = tm6000_i2c_reset(dev, 10); - break; - case XC2028_TUNER_RESET: - /* Reset codes during load firmware */ - switch (arg) { - case 0: - /* newer tuner can faster reset */ - switch (dev->model) { - case TM5600_BOARD_10MOONS_UT821: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x01); - msleep(10); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x00); - msleep(10); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - 0x300, 0x01); - break; - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(60); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(75); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(60); - break; - default: - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - msleep(130); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - msleep(130); - break; - } - - tm6000_ir_wait(dev, 1); - break; - case 1: - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, - 0x02, 0x01); - msleep(10); - break; - case 2: - rc = tm6000_i2c_reset(dev, 100); - break; - } - break; - case XC2028_I2C_FLUSH: - tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); - tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); - break; - } - return rc; -} -EXPORT_SYMBOL_GPL(tm6000_tuner_callback); - -int tm6000_cards_setup(struct tm6000_core *dev) -{ - /* - * Board-specific initialization sequence. Handles all GPIO - * initialization sequences that are board-specific. - * Up to now, all found devices use GPIO1 and GPIO4 at the same way. - * Probably, they're all based on some reference device. Due to that, - * there's a common routine at the end to handle those GPIO's. Devices - * that use different pinups or init sequences can just return at - * the board-specific session. - */ - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - case TM6010_BOARD_GENERIC: - /* Turn xceive 3028 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); - msleep(15); - /* Turn zarlink zl10353 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); - msleep(15); - /* Reset zarlink zl10353 */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); - msleep(15); - /* Turn zarlink zl10353 off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); - msleep(15); - /* ir ? */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); - msleep(15); - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); - msleep(15); - /* DVB led off (orange) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); - msleep(15); - /* Turn zarlink zl10353 on */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); - msleep(15); - /* Reset zarlink zl10353 */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - /* Power led on (blue) */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); - msleep(15); - break; - default: - break; - } - - /* - * Default initialization. Most of the devices seem to use GPIO1 - * and GPIO4.on the same way, so, this handles the common sequence - * used by most devices. - * If a device uses a different sequence or different GPIO pins for - * reset, just add the code at the board-specific part - */ - - if (dev->gpio.tuner_reset) { - int rc; - int i; - - for (i = 0; i < 2; i++) { - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x00); - if (rc < 0) { - printk(KERN_ERR "Error %i doing tuner reset\n", rc); - return rc; - } - - msleep(10); /* Just to be conservative */ - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.tuner_reset, 0x01); - if (rc < 0) { - printk(KERN_ERR "Error %i doing tuner reset\n", rc); - return rc; - } - } - } else { - printk(KERN_ERR "Tuner reset is not configured\n"); - return -1; - } - - msleep(50); - - return 0; -}; - -static void tm6000_config_tuner(struct tm6000_core *dev) -{ - struct tuner_setup tun_setup; - - /* Load tuner module */ - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", dev->tuner_addr, NULL); - - memset(&tun_setup, 0, sizeof(tun_setup)); - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - tun_setup.mode_mask = 0; - if (dev->caps.has_tuner) - tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); - - switch (dev->tuner_type) { - case TUNER_XC2028: - tun_setup.tuner_callback = tm6000_tuner_callback; - break; - case TUNER_XC5000: - tun_setup.tuner_callback = tm6000_xc5000_callback; - break; - } - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); - - switch (dev->tuner_type) { - case TUNER_XC2028: { - struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; - - memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); - memset(&ctl, 0, sizeof(ctl)); - - ctl.demod = XC3028_FE_ZARLINK456; - - xc2028_cfg.tuner = TUNER_XC2028; - xc2028_cfg.priv = &ctl; - - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - ctl.max_len = 80; - ctl.fname = "xc3028L-v36.fw"; - break; - default: - if (dev->dev_type == TM6010) - ctl.fname = "xc3028-v27.fw"; - else - ctl.fname = "xc3028-v24.fw"; - } - - printk(KERN_INFO "Setting firmware parameters for xc2028\n"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, - &xc2028_cfg); - - } - break; - case TUNER_XC5000: - { - struct v4l2_priv_tun_config xc5000_cfg; - struct xc5000_config ctl = { - .i2c_address = dev->tuner_addr, - .if_khz = 4570, - .radio_input = XC5000_RADIO_FM1_MONO, - }; - - xc5000_cfg.tuner = TUNER_XC5000; - xc5000_cfg.priv = &ctl; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, - &xc5000_cfg); - } - break; - default: - printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); - break; - } -} - -static int fill_board_specific_data(struct tm6000_core *dev) -{ - int rc; - - dev->dev_type = tm6000_boards[dev->model].type; - dev->tuner_type = tm6000_boards[dev->model].tuner_type; - dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; - - dev->gpio = tm6000_boards[dev->model].gpio; - - dev->ir_codes = tm6000_boards[dev->model].ir_codes; - - dev->demod_addr = tm6000_boards[dev->model].demod_addr; - - dev->caps = tm6000_boards[dev->model].caps; - - dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; - dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; - dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; - dev->rinput = tm6000_boards[dev->model].rinput; - - /* setup per-model quirks */ - switch (dev->model) { - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_HAUPPAUGE_900H: - dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; - break; - - default: - break; - } - - /* initialize hardware */ - rc = tm6000_init(dev); - if (rc < 0) - return rc; - - return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); -} - - -static void use_alternative_detection_method(struct tm6000_core *dev) -{ - int i, model = -1; - - if (!dev->eedata_size) - return; - - for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { - if (!tm6000_boards[i].eename_size) - continue; - if (dev->eedata_size < tm6000_boards[i].eename_pos + - tm6000_boards[i].eename_size) - continue; - - if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], - tm6000_boards[i].eename, - tm6000_boards[i].eename_size)) { - model = i; - break; - } - } - if (model < 0) { - printk(KERN_INFO "Device has eeprom but is currently unknown\n"); - return; - } - - dev->model = model; - - printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", - tm6000_boards[model].name, model); -} - -#if defined(CONFIG_MODULES) && defined(MODULE) -static void request_module_async(struct work_struct *work) -{ - struct tm6000_core *dev = container_of(work, struct tm6000_core, - request_module_wk); - - request_module("tm6000-alsa"); - - if (dev->caps.has_dvb) - request_module("tm6000-dvb"); -} - -static void request_modules(struct tm6000_core *dev) -{ - INIT_WORK(&dev->request_module_wk, request_module_async); - schedule_work(&dev->request_module_wk); -} - -static void flush_request_modules(struct tm6000_core *dev) -{ - flush_work(&dev->request_module_wk); -} -#else -#define request_modules(dev) -#define flush_request_modules(dev) -#endif /* CONFIG_MODULES */ - -static int tm6000_init_dev(struct tm6000_core *dev) -{ - struct v4l2_frequency f; - int rc = 0; - - mutex_init(&dev->lock); - mutex_lock(&dev->lock); - - if (!is_generic(dev->model)) { - rc = fill_board_specific_data(dev); - if (rc < 0) - goto err; - - /* register i2c bus */ - rc = tm6000_i2c_register(dev); - if (rc < 0) - goto err; - } else { - /* register i2c bus */ - rc = tm6000_i2c_register(dev); - if (rc < 0) - goto err; - - use_alternative_detection_method(dev); - - rc = fill_board_specific_data(dev); - if (rc < 0) - goto err; - } - - /* Default values for STD and resolutions */ - dev->width = 720; - dev->height = 480; - dev->norm = V4L2_STD_NTSC_M; - - /* Configure tuner */ - tm6000_config_tuner(dev); - - /* Set video standard */ - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); - - /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ - f.tuner = 0; - f.type = V4L2_TUNER_ANALOG_TV; - f.frequency = 3092; /* 193.25 MHz */ - dev->freq = f.frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); - - if (dev->caps.has_tda9874) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvaudio", I2C_ADDR_TDA9874, NULL); - - /* register and initialize V4L2 */ - rc = tm6000_v4l2_register(dev); - if (rc < 0) - goto err; - - tm6000_add_into_devlist(dev); - tm6000_init_extension(dev); - - tm6000_ir_init(dev); - - request_modules(dev); - - mutex_unlock(&dev->lock); - return 0; - -err: - mutex_unlock(&dev->lock); - return rc; -} - -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) - -static void get_max_endpoint(struct usb_device *udev, - struct usb_host_interface *alt, - char *msgtype, - struct usb_host_endpoint *curr_e, - struct tm6000_endpoint *tm_ep) -{ - u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); - unsigned int size = tmp & 0x7ff; - - if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult(tmp); - - if (size > tm_ep->maxsize) { - tm_ep->endp = curr_e; - tm_ep->maxsize = size; - tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; - tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; - - printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", - msgtype, curr_e->desc.bEndpointAddress, - size); - } -} - -/* - * tm6000_usb_probe() - * checks for supported devices - */ -static int tm6000_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - struct tm6000_core *dev; - int i, rc; - int nr = 0; - char *speed; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - - /* Selects the proper interface */ - rc = usb_set_interface(usbdev, 0, 1); - if (rc < 0) - goto report_failure; - - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); - if (nr >= TM6000_MAXBOARDS) { - printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); - rc = -ENOMEM; - goto put_device; - } - - /* Create and initialize dev struct */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - rc = -ENOMEM; - goto put_device; - } - spin_lock_init(&dev->slock); - mutex_init(&dev->usb_lock); - - /* Increment usage count */ - set_bit(nr, &tm6000_devused); - snprintf(dev->name, 29, "tm6000 #%d", nr); - - dev->model = id->driver_info; - if (card[nr] < ARRAY_SIZE(tm6000_boards)) - dev->model = card[nr]; - - dev->udev = usbdev; - dev->devno = nr; - - switch (usbdev->speed) { - case USB_SPEED_LOW: - speed = "1.5"; - break; - case USB_SPEED_UNKNOWN: - case USB_SPEED_FULL: - speed = "12"; - break; - case USB_SPEED_HIGH: - speed = "480"; - break; - default: - speed = "unknown"; - } - - /* Get endpoints */ - for (i = 0; i < interface->num_altsetting; i++) { - int ep; - - for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { - struct usb_host_endpoint *e; - int dir_out; - - e = &interface->altsetting[i].endpoint[ep]; - - dir_out = ((e->desc.bEndpointAddress & - USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); - - printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", - i, - interface->altsetting[i].desc.bInterfaceNumber, - interface->altsetting[i].desc.bInterfaceClass); - - switch (e->desc.bmAttributes) { - case USB_ENDPOINT_XFER_BULK: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "Bulk IN", e, - &dev->bulk_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "Bulk OUT", e, - &dev->bulk_out); - } - break; - case USB_ENDPOINT_XFER_ISOC: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "ISOC IN", e, - &dev->isoc_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "ISOC OUT", e, - &dev->isoc_out); - } - break; - case USB_ENDPOINT_XFER_INT: - if (!dir_out) { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "INT IN", e, - &dev->int_in); - } else { - get_max_endpoint(usbdev, - &interface->altsetting[i], - "INT OUT", e, - &dev->int_out); - } - break; - } - } - } - - - printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", - speed, - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct), - interface->altsetting->desc.bInterfaceNumber); - -/* check if the device has the iso in endpoint at the correct place */ - if (!dev->isoc_in.endp) { - printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); - rc = -ENODEV; - goto free_device; - } - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - - printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); - - rc = tm6000_init_dev(dev); - if (rc < 0) - goto free_device; - - return 0; - -free_device: - kfree(dev); -report_failure: - printk(KERN_ERR "tm6000: Error %d while registering\n", rc); - - clear_bit(nr, &tm6000_devused); -put_device: - usb_put_dev(usbdev); - return rc; -} - -/* - * tm6000_usb_disconnect() - * called when the device gets disconnected - * video device will be unregistered on v4l2_close in case it is still open - */ -static void tm6000_usb_disconnect(struct usb_interface *interface) -{ - struct tm6000_core *dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - if (!dev) - return; - - printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); - - flush_request_modules(dev); - - tm6000_ir_fini(dev); - - if (dev->gpio.power_led) { - switch (dev->model) { - case TM6010_BOARD_HAUPPAUGE_900H: - case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: - case TM6010_BOARD_TWINHAN_TU501: - /* Power led off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x01); - msleep(15); - break; - case TM6010_BOARD_BEHOLD_WANDER: - case TM6010_BOARD_BEHOLD_VOYAGER: - case TM6010_BOARD_BEHOLD_WANDER_LITE: - case TM6010_BOARD_BEHOLD_VOYAGER_LITE: - /* Power led off */ - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.power_led, 0x00); - msleep(15); - break; - } - } - tm6000_v4l2_unregister(dev); - - tm6000_i2c_unregister(dev); - - v4l2_device_unregister(&dev->v4l2_dev); - - dev->state |= DEV_DISCONNECTED; - - usb_put_dev(dev->udev); - - tm6000_close_extension(dev); - tm6000_remove_from_devlist(dev); - - clear_bit(dev->devno, &tm6000_devused); - kfree(dev); -} - -static struct usb_driver tm6000_usb_driver = { - .name = "tm6000", - .probe = tm6000_usb_probe, - .disconnect = tm6000_usb_disconnect, - .id_table = tm6000_id_table, -}; - -module_usb_driver(tm6000_usb_driver); - -MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c deleted file mode 100644 index 5c8cbc5d6f72..000000000000 --- a/drivers/media/usb/tm6000/tm6000-core.c +++ /dev/null @@ -1,916 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices -// -// Copyright (c) 2006-2007 Mauro Carvalho Chehab -// -// Copyright (c) 2007 Michel Ludwig -// - DVB-T support - -#include -#include -#include -#include -#include -#include "tm6000.h" -#include "tm6000-regs.h" -#include -#include - -#define USB_TIMEOUT (5 * HZ) /* ms */ - -int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, - u16 value, u16 index, u8 *buf, u16 len) -{ - int ret, i; - unsigned int pipe; - u8 *data = NULL; - int delay = 5000; - - if (len) { - data = kzalloc(len, GFP_KERNEL); - if (!data) - return -ENOMEM; - } - - mutex_lock(&dev->usb_lock); - - if (req_type & USB_DIR_IN) - pipe = usb_rcvctrlpipe(dev->udev, 0); - else { - pipe = usb_sndctrlpipe(dev->udev, 0); - memcpy(data, buf, len); - } - - if (tm6000_debug & V4L2_DEBUG_I2C) { - printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); - - printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", - (req_type & USB_DIR_IN) ? " IN" : "OUT", - req_type, req, value&0xff, value>>8, index&0xff, - index>>8, len&0xff, len>>8); - - if (!(req_type & USB_DIR_IN)) { - printk(KERN_CONT ">>> "); - for (i = 0; i < len; i++) - printk(KERN_CONT " %02x", buf[i]); - printk(KERN_CONT "\n"); - } - } - - ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, - data, len, USB_TIMEOUT); - - if (req_type & USB_DIR_IN) - memcpy(buf, data, len); - - if (tm6000_debug & V4L2_DEBUG_I2C) { - if (ret < 0) { - if (req_type & USB_DIR_IN) - printk(KERN_DEBUG "<<< (len=%d)\n", len); - - printk(KERN_CONT "%s: Error #%d\n", __func__, ret); - } else if (req_type & USB_DIR_IN) { - printk(KERN_CONT "<<< "); - for (i = 0; i < len; i++) - printk(KERN_CONT " %02x", buf[i]); - printk(KERN_CONT "\n"); - } - } - - kfree(data); - - if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) - delay = 0; - - if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { - unsigned int tsleep; - /* Calculate delay time, 14000us for 64 bytes */ - tsleep = (len * 200) + 200; - if (tsleep < delay) - tsleep = delay; - usleep_range(tsleep, tsleep + 1000); - } - else if (delay) - usleep_range(delay, delay + 1000); - - mutex_unlock(&dev->usb_lock); - return ret; -} - -int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - return - tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, - req, value, index, NULL, 0); -} -EXPORT_SYMBOL_GPL(tm6000_set_reg); - -int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[1]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 1); - - if (rc < 0) - return rc; - - return *buf; -} -EXPORT_SYMBOL_GPL(tm6000_get_reg); - -int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, - u16 index, u16 mask) -{ - int rc; - u8 buf[1]; - u8 new_index; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, 0, buf, 1); - - if (rc < 0) - return rc; - - new_index = (buf[0] & ~mask) | (index & mask); - - if (new_index == buf[0]) - return 0; - - return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, - req, value, new_index, NULL, 0); -} -EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); - -int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[2]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 2); - - if (rc < 0) - return rc; - - return buf[1]|buf[0]<<8; -} - -int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) -{ - int rc; - u8 buf[4]; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 4); - - if (rc < 0) - return rc; - - return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; -} - -int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) -{ - int rc; - - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); - if (rc < 0) - return rc; - - msleep(tsleep); - - rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); - msleep(tsleep); - - return rc; -} - -void tm6000_set_fourcc_format(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - int val; - - val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; - if (dev->fourcc == V4L2_PIX_FMT_UYVY) - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); - else - tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); - } else { - if (dev->fourcc == V4L2_PIX_FMT_UYVY) - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); - else - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); - } -} - -static void tm6000_set_vbi(struct tm6000_core *dev) -{ - /* - * FIXME: - * VBI lines and start/end are different between 60Hz and 50Hz - * So, it is very likely that we need to change the config to - * something that takes it into account, doing something different - * if (dev->norm & V4L2_STD_525_60) - */ - - if (dev->dev_type == TM6010) { - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); - tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); - tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); - tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); - tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); - tm6000_set_reg(dev, - TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); - tm6000_set_reg(dev, - TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); - tm6000_set_reg(dev, - TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); - tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); - tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); - tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); - tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); - tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); - } -} - -int tm6000_init_analog_mode(struct tm6000_core *dev) -{ - struct v4l2_frequency f; - - if (dev->dev_type == TM6010) { - u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; - - if (!dev->radio) - active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; - - /* Enable video and audio */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, - active, 0x60); - /* Disable TS input */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, - 0x00, 0x40); - } else { - /* Enables soft reset */ - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - - if (dev->scaler) - /* Disable Hfilter and Enable TS Drop err */ - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); - else /* Enable Hfilter and disable TS Drop err */ - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); - - tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); - tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); - tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); - tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); - tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); - - /* AP Software reset */ - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); - - tm6000_set_fourcc_format(dev); - - /* Disables soft reset */ - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); - } - msleep(20); - - /* Tuner firmware can now be loaded */ - - /* - * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected - * for more than a few seconds. Not sure why, as this behavior does - * not happen on other devices with xc3028. So, I suspect that it - * is yet another bug at tm6000. After start sleeping, decoding - * doesn't start automatically. Instead, it requires some - * I2C commands to wake it up. As we want to have image at the - * beginning, we needed to add this hack. The better would be to - * discover some way to make tm6000 to wake up without this hack. - */ - f.frequency = dev->freq; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); - - msleep(100); - tm6000_set_standard(dev); - tm6000_set_vbi(dev); - tm6000_set_audio_bitrate(dev, 48000); - - /* switch dvb led off */ - if (dev->gpio.dvb_led) { - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.dvb_led, 0x01); - } - - return 0; -} - -int tm6000_init_digital_mode(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - /* Disable video and audio */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, - 0x00, 0x60); - /* Enable TS input */ - tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, - 0x40, 0x40); - /* all power down, but not the digital data port */ - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); - tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); - tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); - } else { - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); - tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); - tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); - tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); - tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); - tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); - tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); - tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); - tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); - tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); - tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); - - tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); - tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); - msleep(50); - - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); - msleep(50); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); - msleep(50); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); - msleep(100); - } - - /* switch dvb led on */ - if (dev->gpio.dvb_led) { - tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, - dev->gpio.dvb_led, 0x00); - } - - return 0; -} -EXPORT_SYMBOL(tm6000_init_digital_mode); - -struct reg_init { - u8 req; - u8 reg; - u8 val; -}; - -/* The meaning of those initializations are unknown */ -static struct reg_init tm6000_init_tab[] = { - /* REG VALUE */ - { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, - { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, - { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, - { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, - { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, - { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, - { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, - { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, - { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, - { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, - { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ - { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, - - { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, - { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, - { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, - { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, - { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, - { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, - { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, - { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, - { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, - { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, - { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, - { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, - { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, - { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, - { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, - { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, - { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, - { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, - { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, - { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, - { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, - { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, - { TM6010_REQ07_RC3_HSTART1, 0x88 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ - { TM6010_REQ05_R18_IMASK7, 0x00 }, -}; - -static struct reg_init tm6010_init_tab[] = { - { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, - { TM6010_REQ07_RC4_HSTART0, 0xa0 }, - { TM6010_REQ07_RC6_HEND0, 0x40 }, - { TM6010_REQ07_RCA_VEND0, 0x31 }, - { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, - { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, - { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, - - { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, - { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, - { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, - { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, - { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, - { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, - { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, - { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, - { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, - - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, - { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, - { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, - { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, - { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, - { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, - { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, - { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, - { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, - { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, - { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, - { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, - { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, - { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, - { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, - { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, - { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, - { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, - { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, - { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, - { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, - { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, - { TM6010_REQ07_RC3_HSTART1, 0x88 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - - { TM6010_REQ05_R18_IMASK7, 0x00 }, - - { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, - { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, - { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, - { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, - { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, - { TM6010_REQ07_RD8_IR, 0x0f }, - - /* set remote wakeup key:any key wakeup */ - { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, - { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, -}; - -int tm6000_init(struct tm6000_core *dev) -{ - int board, rc = 0, i, size; - struct reg_init *tab; - - /* Check board revision */ - board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); - if (board >= 0) { - switch (board & 0xff) { - case 0xf3: - printk(KERN_INFO "Found tm6000\n"); - if (dev->dev_type != TM6000) - dev->dev_type = TM6000; - break; - case 0xf4: - printk(KERN_INFO "Found tm6010\n"); - if (dev->dev_type != TM6010) - dev->dev_type = TM6010; - break; - default: - printk(KERN_INFO "Unknown board version = 0x%08x\n", board); - } - } else - printk(KERN_ERR "Error %i while retrieving board version\n", board); - - if (dev->dev_type == TM6010) { - tab = tm6010_init_tab; - size = ARRAY_SIZE(tm6010_init_tab); - } else { - tab = tm6000_init_tab; - size = ARRAY_SIZE(tm6000_init_tab); - } - - /* Load board's initialization table */ - for (i = 0; i < size; i++) { - rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); - if (rc < 0) { - printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", - rc, - tab[i].req, tab[i].reg, tab[i].val); - return rc; - } - } - - msleep(5); /* Just to be conservative */ - - rc = tm6000_cards_setup(dev); - - return rc; -} - - -int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) -{ - int val = 0; - u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ - u8 areg_0a = 0x91; /* SIF 48KHz */ - - switch (bitrate) { - case 48000: - areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ - areg_0a = 0x91; /* SIF 48KHz */ - dev->audio_bitrate = bitrate; - break; - case 32000: - areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ - areg_0a = 0x90; /* SIF 32KHz */ - dev->audio_bitrate = bitrate; - break; - default: - return -EINVAL; - } - - - /* enable I2S, if we use sif or external I2S device */ - if (dev->dev_type == TM6010) { - val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); - if (val < 0) - return val; - - val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - areg_f0, 0xf0); - if (val < 0) - return val; - } else { - val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, - areg_f0, 0xf0); - if (val < 0) - return val; - } - return 0; -} -EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); - -int tm6000_set_audio_rinput(struct tm6000_core *dev) -{ - if (dev->dev_type == TM6010) { - /* Audio crossbar setting, default SIF1 */ - u8 areg_f0; - u8 areg_07 = 0x10; - - switch (dev->rinput.amux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - areg_f0 = 0x03; - areg_07 = 0x30; - break; - case TM6000_AMUX_ADC1: - areg_f0 = 0x00; - break; - case TM6000_AMUX_ADC2: - areg_f0 = 0x08; - break; - case TM6000_AMUX_I2S: - areg_f0 = 0x04; - break; - default: - printk(KERN_INFO "%s: audio input doesn't support\n", - dev->name); - return 0; - break; - } - /* Set audio input crossbar */ - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - areg_f0, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - areg_07, 0xf0); - } else { - u8 areg_eb; - /* Audio setting, default LINE1 */ - switch (dev->rinput.amux) { - case TM6000_AMUX_ADC1: - areg_eb = 0x00; - break; - case TM6000_AMUX_ADC2: - areg_eb = 0x04; - break; - default: - printk(KERN_INFO "%s: audio input doesn't support\n", - dev->name); - return 0; - break; - } - /* Set audio input */ - tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, - areg_eb, 0x0f); - } - return 0; -} - -static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) -{ - u8 mute_reg = 0; - - if (mute) - mute_reg = 0x08; - - tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); -} - -static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) -{ - u8 mute_reg = 0; - - if (mute) - mute_reg = 0x20; - - if (dev->dev_type == TM6010) { - tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, - mute_reg, 0x20); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, - mute_reg, 0x20); - } else { - tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, - mute_reg, 0x20); - tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, - mute_reg, 0x20); - } -} - -int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) -{ - enum tm6000_mux mux; - - if (dev->radio) - mux = dev->rinput.amux; - else - mux = dev->vinput[dev->input].amux; - - switch (mux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - if (dev->dev_type == TM6010) - tm6010_set_mute_sif(dev, mute); - else { - printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", - dev->name); - return -EINVAL; - } - break; - case TM6000_AMUX_ADC1: - case TM6000_AMUX_ADC2: - tm6010_set_mute_adc(dev, mute); - break; - default: - return -EINVAL; - break; - } - return 0; -} - -static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) -{ - u8 vol_reg; - - vol_reg = vol & 0x0F; - - if (vol < 0) - vol_reg |= 0x40; - - tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); - tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); -} - -static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) -{ - u8 vol_reg; - - vol_reg = (vol + 0x10) & 0x1f; - - if (dev->dev_type == TM6010) { - tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); - tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); - } else { - tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); - tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); - } -} - -void tm6000_set_volume(struct tm6000_core *dev, int vol) -{ - enum tm6000_mux mux; - - if (dev->radio) { - mux = dev->rinput.amux; - vol += 8; /* Offset to 0 dB */ - } else - mux = dev->vinput[dev->input].amux; - - switch (mux) { - case TM6000_AMUX_SIF1: - case TM6000_AMUX_SIF2: - if (dev->dev_type == TM6010) - tm6010_set_volume_sif(dev, vol); - else - printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", - dev->name); - break; - case TM6000_AMUX_ADC1: - case TM6000_AMUX_ADC2: - tm6010_set_volume_adc(dev, vol); - break; - default: - break; - } -} - -static LIST_HEAD(tm6000_devlist); -static DEFINE_MUTEX(tm6000_devlist_mutex); - -/* - * tm6000_realease_resource() - */ - -void tm6000_remove_from_devlist(struct tm6000_core *dev) -{ - mutex_lock(&tm6000_devlist_mutex); - list_del(&dev->devlist); - mutex_unlock(&tm6000_devlist_mutex); -}; - -void tm6000_add_into_devlist(struct tm6000_core *dev) -{ - mutex_lock(&tm6000_devlist_mutex); - list_add_tail(&dev->devlist, &tm6000_devlist); - mutex_unlock(&tm6000_devlist_mutex); -}; - -/* - * Extension interface - */ - -static LIST_HEAD(tm6000_extension_devlist); - -int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, - char *buf, int size) -{ - struct tm6000_ops *ops = NULL; - - /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ - - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fillbuf && ops->type == type) - ops->fillbuf(dev, buf, size); - } - - return 0; -} - -int tm6000_register_extension(struct tm6000_ops *ops) -{ - struct tm6000_core *dev = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_add_tail(&ops->next, &tm6000_extension_devlist); - list_for_each_entry(dev, &tm6000_devlist, devlist) { - ops->init(dev); - printk(KERN_INFO "%s: Initialized (%s) extension\n", - dev->name, ops->name); - } - mutex_unlock(&tm6000_devlist_mutex); - return 0; -} -EXPORT_SYMBOL(tm6000_register_extension); - -void tm6000_unregister_extension(struct tm6000_ops *ops) -{ - struct tm6000_core *dev = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_for_each_entry(dev, &tm6000_devlist, devlist) - ops->fini(dev); - - printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); - list_del(&ops->next); - mutex_unlock(&tm6000_devlist_mutex); -} -EXPORT_SYMBOL(tm6000_unregister_extension); - -void tm6000_init_extension(struct tm6000_core *dev) -{ - struct tm6000_ops *ops = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } - mutex_unlock(&tm6000_devlist_mutex); -} - -void tm6000_close_extension(struct tm6000_core *dev) -{ - struct tm6000_ops *ops = NULL; - - mutex_lock(&tm6000_devlist_mutex); - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } - mutex_unlock(&tm6000_devlist_mutex); -} diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c deleted file mode 100644 index ee04973cbf93..000000000000 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ /dev/null @@ -1,454 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2007 Michel Ludwig - */ - -#include -#include -#include - -#include "tm6000.h" -#include "tm6000-regs.h" - -#include "zl10353.h" - -#include - -#include "xc2028.h" -#include "xc5000.h" - -MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); -MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); - -static int debug; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable debug message"); - -static inline void print_err_status(struct tm6000_core *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(dev, 1, "URB status %d [%s].\n", - status, errmsg); - } else { - dprintk(dev, 1, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - -static void tm6000_urb_received(struct urb *urb) -{ - int ret; - struct tm6000_core *dev = urb->context; - - switch (urb->status) { - case 0: - case -ETIMEDOUT: - break; - case -ENOENT: - case -ECONNRESET: - case -ESHUTDOWN: - return; - default: - print_err_status(dev, 0, urb->status); - } - - if (urb->actual_length > 0) - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, - urb->actual_length); - - if (dev->dvb->streams > 0) { - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) { - printk(KERN_ERR "tm6000: error %s\n", __func__); - kfree(urb->transfer_buffer); - usb_free_urb(urb); - dev->dvb->bulk_urb = NULL; - } - } -} - -static int tm6000_start_stream(struct tm6000_core *dev) -{ - int ret; - unsigned int pipe, size; - struct tm6000_dvb *dvb = dev->dvb; - - printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); - - if (dev->mode != TM6000_MODE_DIGITAL) { - tm6000_init_digital_mode(dev); - dev->mode = TM6000_MODE_DIGITAL; - } - - dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dvb->bulk_urb) - return -ENOMEM; - - pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe); - size = size * 15; /* 512 x 8 or 12 or 15 */ - - dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); - if (!dvb->bulk_urb->transfer_buffer) { - usb_free_urb(dvb->bulk_urb); - dvb->bulk_urb = NULL; - return -ENOMEM; - } - - usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, - dvb->bulk_urb->transfer_buffer, - size, - tm6000_urb_received, dev); - - ret = usb_clear_halt(dev->udev, pipe); - if (ret < 0) { - printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", - ret, __func__); - - kfree(dvb->bulk_urb->transfer_buffer); - usb_free_urb(dvb->bulk_urb); - dvb->bulk_urb = NULL; - return ret; - } else - printk(KERN_ERR "tm6000: pipe reset\n"); - -/* mutex_lock(&tm6000_driver.open_close_mutex); */ - ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC); - -/* mutex_unlock(&tm6000_driver.open_close_mutex); */ - if (ret) { - printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", - ret); - - kfree(dvb->bulk_urb->transfer_buffer); - usb_free_urb(dvb->bulk_urb); - dvb->bulk_urb = NULL; - return ret; - } - - return 0; -} - -static void tm6000_stop_stream(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dvb->bulk_urb) { - printk(KERN_INFO "urb killing\n"); - usb_kill_urb(dvb->bulk_urb); - printk(KERN_INFO "urb buffer free\n"); - kfree(dvb->bulk_urb->transfer_buffer); - usb_free_urb(dvb->bulk_urb); - dvb->bulk_urb = NULL; - } -} - -static int tm6000_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct tm6000_core *dev = demux->priv; - struct tm6000_dvb *dvb = dev->dvb; - printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); - - mutex_lock(&dvb->mutex); - if (dvb->streams == 0) { - dvb->streams = 1; -/* mutex_init(&tm6000_dev->streming_mutex); */ - tm6000_start_stream(dev); - } else - ++(dvb->streams); - mutex_unlock(&dvb->mutex); - - return 0; -} - -static int tm6000_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct tm6000_core *dev = demux->priv; - struct tm6000_dvb *dvb = dev->dvb; - - printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); - - mutex_lock(&dvb->mutex); - - printk(KERN_INFO "stream %#x\n", dvb->streams); - --(dvb->streams); - if (dvb->streams == 0) { - printk(KERN_INFO "stop stream\n"); - tm6000_stop_stream(dev); -/* mutex_destroy(&tm6000_dev->streaming_mutex); */ - } - mutex_unlock(&dvb->mutex); -/* mutex_destroy(&tm6000_dev->streaming_mutex); */ - - return 0; -} - -static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dev->caps.has_zl10353) { - struct zl10353_config config = { - .demod_address = dev->demod_addr, - .no_tuner = 1, - .parallel_ts = 1, - .if2 = 45700, - .disable_i2c_gate_ctrl = 1, - }; - - dvb->frontend = dvb_attach(zl10353_attach, &config, - &dev->i2c_adap); - } else { - printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); - return -1; - } - - return (!dvb->frontend) ? -1 : 0; -} - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static int register_dvb(struct tm6000_core *dev) -{ - int ret = -1; - struct tm6000_dvb *dvb = dev->dvb; - - mutex_init(&dvb->mutex); - - dvb->streams = 0; - - /* attach the frontend */ - ret = tm6000_dvb_attach_frontend(dev); - if (ret < 0) { - printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); - goto err; - } - - ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", - THIS_MODULE, &dev->udev->dev, adapter_nr); - if (ret < 0) { - pr_err("tm6000: couldn't register the adapter!\n"); - goto err; - } - - dvb->adapter.priv = dev; - - if (dvb->frontend) { - switch (dev->tuner_type) { - case TUNER_XC2028: { - struct xc2028_config cfg = { - .i2c_adap = &dev->i2c_adap, - .i2c_addr = dev->tuner_addr, - }; - - dvb->frontend->callback = tm6000_tuner_callback; - ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (ret < 0) { - printk(KERN_ERR - "tm6000: couldn't register frontend\n"); - goto adapter_err; - } - - if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { - printk(KERN_ERR "tm6000: couldn't register frontend (xc3028)\n"); - ret = -EINVAL; - goto frontend_err; - } - printk(KERN_INFO "tm6000: XC2028/3028 asked to be attached to frontend!\n"); - break; - } - case TUNER_XC5000: { - struct xc5000_config cfg = { - .i2c_address = dev->tuner_addr, - }; - - dvb->frontend->callback = tm6000_xc5000_callback; - ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); - if (ret < 0) { - printk(KERN_ERR - "tm6000: couldn't register frontend\n"); - goto adapter_err; - } - - if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { - printk(KERN_ERR "tm6000: couldn't register frontend (xc5000)\n"); - ret = -EINVAL; - goto frontend_err; - } - printk(KERN_INFO "tm6000: XC5000 asked to be attached to frontend!\n"); - break; - } - } - } else - printk(KERN_ERR "tm6000: no frontend found\n"); - - dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING - | DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dev; - dvb->demux.filternum = 8; - dvb->demux.feednum = 8; - dvb->demux.start_feed = tm6000_start_feed; - dvb->demux.stop_feed = tm6000_stop_feed; - dvb->demux.write_to_decoder = NULL; - ret = dvb_dmx_init(&dvb->demux); - if (ret < 0) { - printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); - goto frontend_err; - } - - dvb->dmxdev.filternum = dev->dvb->demux.filternum; - dvb->dmxdev.demux = &dev->dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if (ret < 0) { - printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); - goto dvb_dmx_err; - } - - return 0; - -dvb_dmx_err: - dvb_dmx_release(&dvb->demux); -frontend_err: - if (dvb->frontend) { - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); - } -adapter_err: - dvb_unregister_adapter(&dvb->adapter); -err: - return ret; -} - -static void unregister_dvb(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb = dev->dvb; - - if (dvb->bulk_urb) { - struct urb *bulk_urb = dvb->bulk_urb; - - kfree(bulk_urb->transfer_buffer); - bulk_urb->transfer_buffer = NULL; - usb_unlink_urb(bulk_urb); - usb_free_urb(bulk_urb); - } - -/* mutex_lock(&tm6000_driver.open_close_mutex); */ - if (dvb->frontend) { - dvb_unregister_frontend(dvb->frontend); - dvb_frontend_detach(dvb->frontend); - } - - dvb_dmxdev_release(&dvb->dmxdev); - dvb_dmx_release(&dvb->demux); - dvb_unregister_adapter(&dvb->adapter); - mutex_destroy(&dvb->mutex); -/* mutex_unlock(&tm6000_driver.open_close_mutex); */ -} - -static int dvb_init(struct tm6000_core *dev) -{ - struct tm6000_dvb *dvb; - int rc; - - if (!dev) - return 0; - - if (!dev->caps.has_dvb) - return 0; - - if (dev->udev->speed == USB_SPEED_FULL) { - printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n"); - return 0; - } - - dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); - if (!dvb) - return -ENOMEM; - - dev->dvb = dvb; - - rc = register_dvb(dev); - if (rc < 0) { - kfree(dvb); - dev->dvb = NULL; - return 0; - } - - return 0; -} - -static int dvb_fini(struct tm6000_core *dev) -{ - if (!dev) - return 0; - - if (!dev->caps.has_dvb) - return 0; - - if (dev->dvb) { - unregister_dvb(dev); - kfree(dev->dvb); - dev->dvb = NULL; - } - - return 0; -} - -static struct tm6000_ops dvb_ops = { - .type = TM6000_DVB, - .name = "TM6000 dvb Extension", - .init = dvb_init, - .fini = dvb_fini, -}; - -static int __init tm6000_dvb_register(void) -{ - return tm6000_register_extension(&dvb_ops); -} - -static void __exit tm6000_dvb_unregister(void) -{ - tm6000_unregister_extension(&dvb_ops); -} - -module_init(tm6000_dvb_register); -module_exit(tm6000_dvb_unregister); diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c deleted file mode 100644 index 7554b93b82e6..000000000000 --- a/drivers/media/usb/tm6000/tm6000-i2c.c +++ /dev/null @@ -1,317 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices -// -// Copyright (c) 2006-2007 Mauro Carvalho Chehab -// -// Copyright (c) 2007 Michel Ludwig -// - Fix SMBus Read Byte command - -#include -#include -#include -#include - -#include "tm6000.h" -#include "tm6000-regs.h" -#include -#include -#include "xc2028.h" - - -/* ----------------------------------------------------------- */ - -static unsigned int i2c_debug; -module_param(i2c_debug, int, 0644); -MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); - -#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__, ##args); } while (0) - -static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, - __u8 reg, char *buf, int len) -{ - int rc; - unsigned int i2c_packet_limit = 16; - - if (dev->dev_type == TM6010) - i2c_packet_limit = 80; - - if (!buf) - return -1; - - if (len < 1 || len > i2c_packet_limit) { - printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", - len, i2c_packet_limit); - return -1; - } - - /* capture mutex */ - rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, - addr | reg << 8, 0, buf, len); - - if (rc < 0) { - /* release mutex */ - return rc; - } - - /* release mutex */ - return rc; -} - -/* Generic read - doesn't work fine with 16bit registers */ -static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, - __u8 reg, char *buf, int len) -{ - int rc; - u8 b[2]; - unsigned int i2c_packet_limit = 16; - - if (dev->dev_type == TM6010) - i2c_packet_limit = 64; - - if (!buf) - return -1; - - if (len < 1 || len > i2c_packet_limit) { - printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", - len, i2c_packet_limit); - return -1; - } - - /* capture mutex */ - if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { - /* - * Workaround an I2C bug when reading from zl10353 - */ - reg -= 1; - len += 1; - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); - - *buf = b[1]; - } else { - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); - } - - /* release mutex */ - return rc; -} - -/* - * read from a 16bit register - * for example xc2028, xc3028 or xc3028L - */ -static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, - __u16 reg, char *buf, int len) -{ - int rc; - unsigned char ureg; - - if (!buf || len != 2) - return -1; - - /* capture mutex */ - if (dev->dev_type == TM6010) { - ureg = reg & 0xFF; - rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, - addr | (reg & 0xFF00), 0, &ureg, 1); - - if (rc < 0) { - /* release mutex */ - return rc; - } - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, - reg, 0, buf, len); - } else { - rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, - addr, reg, buf, len); - } - - /* release mutex */ - return rc; -} - -static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msgs[], int num) -{ - struct tm6000_core *dev = i2c_adap->algo_data; - int addr, rc, i, byte; - - for (i = 0; i < num; i++) { - addr = (msgs[i].addr << 1) & 0xff; - i2c_dprintk(2, "%s %s addr=0x%x len=%d:", - (msgs[i].flags & I2C_M_RD) ? "read" : "write", - i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (msgs[i].flags & I2C_M_RD) { - /* read request without preceding register selection */ - /* - * The TM6000 only supports a read transaction - * immediately after a 1 or 2 byte write to select - * a register. We cannot fulfill this request. - */ - i2c_dprintk(2, " read without preceding write not supported"); - rc = -EOPNOTSUPP; - goto err; - } else if (i + 1 < num && msgs[i].len <= 2 && - (msgs[i + 1].flags & I2C_M_RD) && - msgs[i].addr == msgs[i + 1].addr) { - /* 1 or 2 byte write followed by a read */ - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - i2c_dprintk(2, "; joined to read %s len=%d:", - i == num - 2 ? "stop" : "nonstop", - msgs[i + 1].len); - - if (msgs[i].len == 2) { - rc = tm6000_i2c_recv_regs16(dev, addr, - msgs[i].buf[0] << 8 | msgs[i].buf[1], - msgs[i + 1].buf, msgs[i + 1].len); - } else { - rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], - msgs[i + 1].buf, msgs[i + 1].len); - } - - i++; - - if (addr == dev->tuner_addr << 1) { - tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); - tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); - } - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - } else { - /* write bytes */ - if (i2c_debug >= 2) - for (byte = 0; byte < msgs[i].len; byte++) - printk(KERN_CONT " %02x", msgs[i].buf[byte]); - rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], - msgs[i].buf + 1, msgs[i].len - 1); - } - if (i2c_debug >= 2) - printk(KERN_CONT "\n"); - if (rc < 0) - goto err; - } - - return num; -err: - i2c_dprintk(2, " ERROR: %i\n", rc); - return rc; -} - -static int tm6000_i2c_eeprom(struct tm6000_core *dev) -{ - int i, rc; - unsigned char *p = dev->eedata; - unsigned char bytes[17]; - - dev->i2c_client.addr = 0xa0 >> 1; - dev->eedata_size = 0; - - bytes[16] = '\0'; - for (i = 0; i < sizeof(dev->eedata); ) { - *p = i; - rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); - if (rc < 1) { - if (p == dev->eedata) - goto noeeprom; - else { - printk(KERN_WARNING - "%s: i2c eeprom read error (err=%d)\n", - dev->name, rc); - } - return -EINVAL; - } - dev->eedata_size++; - p++; - if (0 == (i % 16)) - printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); - printk(KERN_CONT " %02x", dev->eedata[i]); - if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) - bytes[i%16] = dev->eedata[i]; - else - bytes[i%16] = '.'; - - i++; - - if (0 == (i % 16)) { - bytes[16] = '\0'; - printk(KERN_CONT " %s\n", bytes); - } - } - if (0 != (i%16)) { - bytes[i%16] = '\0'; - for (i %= 16; i < 16; i++) - printk(KERN_CONT " "); - printk(KERN_CONT " %s\n", bytes); - } - - return 0; - -noeeprom: - printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", - dev->name, rc); - return -EINVAL; -} - -/* ----------------------------------------------------------- */ - -/* - * functionality() - */ -static u32 functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static const struct i2c_algorithm tm6000_algo = { - .master_xfer = tm6000_i2c_xfer, - .functionality = functionality, -}; - -/* ----------------------------------------------------------- */ - -/* - * tm6000_i2c_register() - * register i2c bus - */ -int tm6000_i2c_register(struct tm6000_core *dev) -{ - int rc; - - dev->i2c_adap.owner = THIS_MODULE; - dev->i2c_adap.algo = &tm6000_algo; - dev->i2c_adap.dev.parent = &dev->udev->dev; - strscpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); - dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); - rc = i2c_add_adapter(&dev->i2c_adap); - if (rc) - return rc; - - dev->i2c_client.adapter = &dev->i2c_adap; - strscpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); - tm6000_i2c_eeprom(dev); - - return 0; -} - -/* - * tm6000_i2c_unregister() - * unregister i2c_bus - */ -int tm6000_i2c_unregister(struct tm6000_core *dev) -{ - i2c_del_adapter(&dev->i2c_adap); - return 0; -} diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c deleted file mode 100644 index 5136e9e202f1..000000000000 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (C) 2010 Stefan Ringel - */ - -#include -#include -#include - -#include -#include - -#include - -#include "tm6000.h" -#include "tm6000-regs.h" - -static unsigned int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "debug message level"); - -static unsigned int enable_ir = 1; -module_param(enable_ir, int, 0644); -MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); - -static unsigned int ir_clock_mhz = 12; -module_param(ir_clock_mhz, int, 0644); -MODULE_PARM_DESC(ir_clock_mhz, "ir clock, in MHz"); - -#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ -#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ - -#undef dprintk - -#define dprintk(level, fmt, arg...) do {\ - if (ir_debug >= level) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } while (0) - -struct tm6000_ir_poll_result { - u16 rc_data; -}; - -struct tm6000_IR { - struct tm6000_core *dev; - struct rc_dev *rc; - char name[32]; - char phys[32]; - - /* poll expernal decoder */ - int polling; - struct delayed_work work; - u8 wait:1; - u8 pwled:2; - u8 submit_urb:1; - struct urb *int_urb; - - /* IR device properties */ - u64 rc_proto; -}; - -void tm6000_ir_wait(struct tm6000_core *dev, u8 state) -{ - struct tm6000_IR *ir = dev->ir; - - if (!dev->ir) - return; - - dprintk(2, "%s: %i\n",__func__, ir->wait); - - if (state) - ir->wait = 1; - else - ir->wait = 0; -} - -static int tm6000_ir_config(struct tm6000_IR *ir) -{ - struct tm6000_core *dev = ir->dev; - u32 pulse = 0, leader = 0; - - dprintk(2, "%s\n",__func__); - - /* - * The IR decoder supports RC-5 or NEC, with a configurable timing. - * The timing configuration there is not that accurate, as it uses - * approximate values. The NEC spec mentions a 562.5 unit period, - * and RC-5 uses a 888.8 period. - * Currently, driver assumes a clock provided by a 12 MHz XTAL, but - * a modprobe parameter can adjust it. - * Adjustments are required for other timings. - * It seems that the 900ms timing for NEC is used to detect a RC-5 - * IR, in order to discard such decoding - */ - - switch (ir->rc_proto) { - case RC_PROTO_BIT_NEC: - leader = 900; /* ms */ - pulse = 700; /* ms - the actual value would be 562 */ - break; - default: - case RC_PROTO_BIT_RC5: - leader = 900; /* ms - from the NEC decoding */ - pulse = 1780; /* ms - The actual value would be 1776 */ - break; - } - - pulse = ir_clock_mhz * pulse; - leader = ir_clock_mhz * leader; - if (ir->rc_proto == RC_PROTO_BIT_NEC) - leader = leader | 0x8000; - - dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", - __func__, - (ir->rc_proto == RC_PROTO_BIT_NEC) ? "NEC" : "RC-5", - ir_clock_mhz, leader, pulse); - - /* Remote WAKEUP = enable, normal mode, from IR decoder output */ - tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); - - /* Enable IR reception on non-busrt mode */ - tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); - - /* IR_WKUP_SEL = Low byte in decoded IR data */ - tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); - /* IR_WKU_ADD code */ - tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); - - tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); - tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); - - tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); - tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); - - if (!ir->polling) - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); - else - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); - msleep(10); - - /* Shows that IR is working via the LED */ - tm6000_flash_led(dev, 0); - msleep(100); - tm6000_flash_led(dev, 1); - ir->pwled = 1; - - return 0; -} - -static void tm6000_ir_keydown(struct tm6000_IR *ir, - const char *buf, unsigned int len) -{ - u8 device, command; - u32 scancode; - enum rc_proto protocol; - - if (len < 1) - return; - - command = buf[0]; - device = (len > 1 ? buf[1] : 0x0); - switch (ir->rc_proto) { - case RC_PROTO_BIT_RC5: - protocol = RC_PROTO_RC5; - scancode = RC_SCANCODE_RC5(device, command); - break; - case RC_PROTO_BIT_NEC: - protocol = RC_PROTO_NEC; - scancode = RC_SCANCODE_NEC(device, command); - break; - default: - protocol = RC_PROTO_OTHER; - scancode = RC_SCANCODE_OTHER(device << 8 | command); - break; - } - - dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", - __func__, protocol, scancode); - rc_keydown(ir->rc, protocol, scancode, 0); -} - -static void tm6000_ir_urb_received(struct urb *urb) -{ - struct tm6000_core *dev = urb->context; - struct tm6000_IR *ir = dev->ir; - char *buf; - - dprintk(2, "%s\n",__func__); - if (urb->status < 0 || urb->actual_length <= 0) { - printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", - urb->status, urb->actual_length); - ir->submit_urb = 1; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - return; - } - buf = urb->transfer_buffer; - - if (ir_debug) - print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", - DUMP_PREFIX_OFFSET,16, 1, - buf, urb->actual_length, false); - - tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); - - usb_submit_urb(urb, GFP_ATOMIC); - /* - * Flash the led. We can't do it here, as it is running on IRQ context. - * So, use the scheduler to do it, in a few ms. - */ - ir->pwled = 2; - schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); -} - -static void tm6000_ir_handle_key(struct work_struct *work) -{ - struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); - struct tm6000_core *dev = ir->dev; - int rc; - u8 buf[2]; - - if (ir->wait) - return; - - dprintk(3, "%s\n",__func__); - - rc = tm6000_read_write_usb(dev, USB_DIR_IN | - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - REQ_02_GET_IR_CODE, 0, 0, buf, 2); - if (rc < 0) - return; - - /* Check if something was read */ - if ((buf[0] & 0xff) == 0xff) { - if (!ir->pwled) { - tm6000_flash_led(dev, 1); - ir->pwled = 1; - } - return; - } - - tm6000_ir_keydown(ir, buf, rc); - tm6000_flash_led(dev, 0); - ir->pwled = 0; - - /* Re-schedule polling */ - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); -} - -static void tm6000_ir_int_work(struct work_struct *work) -{ - struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); - struct tm6000_core *dev = ir->dev; - int rc; - - dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, - ir->pwled); - - if (ir->submit_urb) { - dprintk(3, "Resubmit urb\n"); - tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); - - rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); - if (rc < 0) { - printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", - rc); - /* Retry in 100 ms */ - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - return; - } - ir->submit_urb = 0; - } - - /* Led is enabled only if USB submit doesn't fail */ - if (ir->pwled == 2) { - tm6000_flash_led(dev, 0); - ir->pwled = 0; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); - } else if (!ir->pwled) { - tm6000_flash_led(dev, 1); - ir->pwled = 1; - } -} - -static int tm6000_ir_start(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - dprintk(2, "%s\n",__func__); - - schedule_delayed_work(&ir->work, 0); - - return 0; -} - -static void tm6000_ir_stop(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - dprintk(2, "%s\n",__func__); - - cancel_delayed_work_sync(&ir->work); -} - -static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) -{ - struct tm6000_IR *ir = rc->priv; - - if (!ir) - return 0; - - dprintk(2, "%s\n",__func__); - - ir->rc_proto = *rc_proto; - - tm6000_ir_config(ir); - /* TODO */ - return 0; -} - -static int __tm6000_ir_int_start(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - struct tm6000_core *dev; - int pipe, size; - int err = -ENOMEM; - - if (!ir) - return -ENODEV; - dev = ir->dev; - - dprintk(2, "%s\n",__func__); - - ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!ir->int_urb) - return -ENOMEM; - - pipe = usb_rcvintpipe(dev->udev, - dev->int_in.endp->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe); - dprintk(1, "IR max size: %d\n", size); - - ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); - if (!ir->int_urb->transfer_buffer) { - usb_free_urb(ir->int_urb); - return err; - } - dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); - - usb_fill_int_urb(ir->int_urb, dev->udev, pipe, - ir->int_urb->transfer_buffer, size, - tm6000_ir_urb_received, dev, - dev->int_in.endp->desc.bInterval); - - ir->submit_urb = 1; - schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); - - return 0; -} - -static void __tm6000_ir_int_stop(struct rc_dev *rc) -{ - struct tm6000_IR *ir = rc->priv; - - if (!ir || !ir->int_urb) - return; - - dprintk(2, "%s\n",__func__); - - usb_kill_urb(ir->int_urb); - kfree(ir->int_urb->transfer_buffer); - usb_free_urb(ir->int_urb); - ir->int_urb = NULL; -} - -int tm6000_ir_int_start(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - if (!ir) - return 0; - - return __tm6000_ir_int_start(ir->rc); -} - -void tm6000_ir_int_stop(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - if (!ir || !ir->rc) - return; - - __tm6000_ir_int_stop(ir->rc); -} - -int tm6000_ir_init(struct tm6000_core *dev) -{ - struct tm6000_IR *ir; - struct rc_dev *rc; - int err = -ENOMEM; - u64 rc_proto; - - if (!enable_ir) - return -ENODEV; - - if (!dev->caps.has_remote) - return 0; - - if (!dev->ir_codes) - return 0; - - ir = kzalloc(sizeof(*ir), GFP_ATOMIC); - rc = rc_allocate_device(RC_DRIVER_SCANCODE); - if (!ir || !rc) - goto out; - - dprintk(2, "%s\n", __func__); - - /* record handles to ourself */ - ir->dev = dev; - dev->ir = ir; - ir->rc = rc; - - /* input setup */ - rc->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_NEC; - /* Needed, in order to support NEC remotes with 24 or 32 bits */ - rc->scancode_mask = 0xffff; - rc->priv = ir; - rc->change_protocol = tm6000_ir_change_protocol; - if (dev->int_in.endp) { - rc->open = __tm6000_ir_int_start; - rc->close = __tm6000_ir_int_stop; - INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); - } else { - rc->open = tm6000_ir_start; - rc->close = tm6000_ir_stop; - ir->polling = 50; - INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); - } - - snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", - dev->name); - - usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); - strlcat(ir->phys, "/input0", sizeof(ir->phys)); - - rc_proto = RC_PROTO_BIT_UNKNOWN; - tm6000_ir_change_protocol(rc, &rc_proto); - - rc->device_name = ir->name; - rc->input_phys = ir->phys; - rc->input_id.bustype = BUS_USB; - rc->input_id.version = 1; - rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - rc->map_name = dev->ir_codes; - rc->driver_name = "tm6000"; - rc->dev.parent = &dev->udev->dev; - - /* ir register */ - err = rc_register_device(rc); - if (err) - goto out; - - return 0; - -out: - dev->ir = NULL; - rc_free_device(rc); - kfree(ir); - return err; -} - -int tm6000_ir_fini(struct tm6000_core *dev) -{ - struct tm6000_IR *ir = dev->ir; - - /* skip detach on non attached board */ - - if (!ir) - return 0; - - dprintk(2, "%s\n",__func__); - - if (!ir->polling) - __tm6000_ir_int_stop(ir->rc); - - tm6000_ir_stop(ir->rc); - - /* Turn off the led */ - tm6000_flash_led(dev, 0); - ir->pwled = 0; - - rc_unregister_device(ir->rc); - - kfree(ir); - dev->ir = NULL; - - return 0; -} diff --git a/drivers/media/usb/tm6000/tm6000-regs.h b/drivers/media/usb/tm6000/tm6000-regs.h deleted file mode 100644 index 6a181f2e7ef2..000000000000 --- a/drivers/media/usb/tm6000/tm6000-regs.h +++ /dev/null @@ -1,588 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (c) 2006-2007 Mauro Carvalho Chehab - */ - -/* - * Define TV Master TM5600/TM6000/TM6010 Request codes - */ -#define REQ_00_SET_IR_VALUE 0 -#define REQ_01_SET_WAKEUP_IRCODE 1 -#define REQ_02_GET_IR_CODE 2 -#define REQ_03_SET_GET_MCU_PIN 3 -#define REQ_04_EN_DISABLE_MCU_INT 4 -#define REQ_05_SET_GET_USBREG 5 - /* Write: RegNum, Value, 0 */ - /* Read : RegNum, Value, 1, RegStatus */ -#define REQ_06_SET_GET_USBREG_BIT 6 -#define REQ_07_SET_GET_AVREG 7 - /* Write: RegNum, Value, 0 */ - /* Read : RegNum, Value, 1, RegStatus */ -#define REQ_08_SET_GET_AVREG_BIT 8 -#define REQ_09_SET_GET_TUNER_FQ 9 -#define REQ_10_SET_TUNER_SYSTEM 10 -#define REQ_11_SET_EEPROM_ADDR 11 -#define REQ_12_SET_GET_EEPROMBYTE 12 -#define REQ_13_GET_EEPROM_SEQREAD 13 -#define REQ_14_SET_GET_I2C_WR2_RDN 14 -#define REQ_15_SET_GET_I2CBYTE 15 - /* Write: Subaddr, Slave Addr, value, 0 */ - /* Read : Subaddr, Slave Addr, value, 1 */ -#define REQ_16_SET_GET_I2C_WR1_RDN 16 - /* Subaddr, Slave Addr, 0, length */ -#define REQ_17_SET_GET_I2CFP 17 - /* Write: Slave Addr, register, value */ - /* Read : Slave Addr, register, 2, data */ -#define REQ_20_DATA_TRANSFER 20 -#define REQ_30_I2C_WRITE 30 -#define REQ_31_I2C_READ 31 -#define REQ_35_AFTEK_TUNER_READ 35 -#define REQ_40_GET_VERSION 40 -#define REQ_50_SET_START 50 -#define REQ_51_SET_STOP 51 -#define REQ_52_TRANSMIT_DATA 52 -#define REQ_53_SPI_INITIAL 53 -#define REQ_54_SPI_SETSTART 54 -#define REQ_55_SPI_INOUTDATA 55 -#define REQ_56_SPI_SETSTOP 56 - -/* - * Define TV Master TM5600/TM6000/TM6010 GPIO lines - */ - -#define TM6000_GPIO_CLK 0x101 -#define TM6000_GPIO_DATA 0x100 - -#define TM6000_GPIO_1 0x102 -#define TM6000_GPIO_2 0x103 -#define TM6000_GPIO_3 0x104 -#define TM6000_GPIO_4 0x300 -#define TM6000_GPIO_5 0x301 -#define TM6000_GPIO_6 0x304 -#define TM6000_GPIO_7 0x305 - -/* tm6010 defines GPIO with different values */ -#define TM6010_GPIO_0 0x0102 -#define TM6010_GPIO_1 0x0103 -#define TM6010_GPIO_2 0x0104 -#define TM6010_GPIO_3 0x0105 -#define TM6010_GPIO_4 0x0106 -#define TM6010_GPIO_5 0x0107 -#define TM6010_GPIO_6 0x0300 -#define TM6010_GPIO_7 0x0301 -#define TM6010_GPIO_9 0x0305 -/* - * Define TV Master TM5600/TM6000/TM6010 URB message codes and length - */ - -enum { - TM6000_URB_MSG_VIDEO = 1, - TM6000_URB_MSG_AUDIO, - TM6000_URB_MSG_VBI, - TM6000_URB_MSG_PTS, - TM6000_URB_MSG_ERR, -}; - -/* Define specific TM6000 Video decoder registers */ -#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 -#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 -#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda -#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb -#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc -#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd -#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde -#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf -#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 -#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 -#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 -#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 -#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 -#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 -#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 -#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 -#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 -#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 -#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea -#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb -#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec -#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed -#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee -#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef -#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd -#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe - -/* Define TM6000/TM6010 Video decoder registers */ -#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 -#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 -#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 -#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 -#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 -#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 -#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 -#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 -#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 -#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 -#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a -#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b -#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c -#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d -#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f -#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 -#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 -#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 -#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 -#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 -#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 -#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 -#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 -#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 -#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 -#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a -#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b -#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c -#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d -#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e -#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f -#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 -#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 -#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 -#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 -#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 -#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 -#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 -#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 -#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 -#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 -#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a -#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b -#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c -#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d -#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e -#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f -#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 -#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 -#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 -#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 -#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 -#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 -#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 -#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 -#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 -#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 -#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a -#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b -#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c -#define TM6010_REQ07_R3F_RESET 0x07, 0x3f -#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 -#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 -#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 -#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 -#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 -#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 -#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 -#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 -#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 -#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 -#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a -#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b -#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c -#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d -#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e -#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f -#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 -#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 -#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 -#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 -#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 -#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 -#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 -#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 -#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 -#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 -#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a -#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b -#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c -#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d -#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e -#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f -#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 -#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 -#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 -#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 -#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 -#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 -#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 -#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 -#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 -#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 -#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 -#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 -#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 -#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 -#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 -#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 -#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 -#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 -#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 -#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a -#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b -#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c -#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d -#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f -#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 -#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 -#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 -#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 -#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 -#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 -#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 -#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a -#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b -#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d -#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e -#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f - -/* Define TM6000/TM6010 Miscellaneous registers */ -#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 -#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 -#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 -#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 -#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 -#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 -#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 -#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 -#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 -#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 -#define TM6010_REQ07_RCA_VEND0 0x07, 0xca -#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb -/* ONLY for TM6010 */ -#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc -#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) -#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) -#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 -#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 -#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 -#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 -#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 -#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 -#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 -#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RD8_IR 0x07, 0xd8 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde -/* ONLY for TM6010 */ -#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 -/* ONLY for TM6010 */ -#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 -/* ONLY for TM6010 */ -#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe -#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff - -/* Define TM6000/TM6010 USB registers */ -#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 -#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 -#define TM6010_REQ05_R02_TEST 0x05, 0x02 -#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 -#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 -#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 -#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 -#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 -#define TM6010_REQ05_R09_VCTL 0x05, 0x09 -#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a -#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b -#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c -#define TM6010_REQ05_R10_GMASK 0x05, 0x10 -#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 -#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 -#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 -#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 -#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 -#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 -#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 -#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 -#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 -#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a -#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c -#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d -#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 -#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 -#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 -#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 -#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 -#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 -#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 -#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 -#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 -#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 -#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a -#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b -#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c -#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d -#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e -#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f -#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 -#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 -#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 -#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 -#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 -#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 -#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 -#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 -#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 -#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 -#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a -#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b -#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c -#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d -#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e -#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 -#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 -#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 -#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 -#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 -#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 -#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 -#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 -#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 -#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a -#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b -#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c -#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d -#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e -#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f -#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 -#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 -#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 -#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 -#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 -#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 -#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 -#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a -#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b -#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c -#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d -#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 -#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 -#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 -#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 -#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 -#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 -#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 -#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 -#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 -#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a -#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b -#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c -#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d -#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e -#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f -#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 -#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 -#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b -#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c -#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d -#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 -#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 -#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 -#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 -#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 -#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 -#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 -#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 -#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 -#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 -#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a -#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b -#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c -#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d -#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e -#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f -#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 -#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 -#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 -#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 -#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 -#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 -#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 -#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 -#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 -#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 -#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a -#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b -#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c -#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d -#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e -#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f -#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 -#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 -#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 -#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 -#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 -#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 -#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 -#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 -#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 -#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 -#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa -#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab -#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac -#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad -#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae -#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf -#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 -#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 -#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 -#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 -#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 -#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 -#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 -#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 -#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 -#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 -#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba -#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb -#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc -#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd -#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe -#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf -#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 -#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 -#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 -#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc -#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 -#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 -#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 -#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc -#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 -#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 -#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 -#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec -#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 -#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 -#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 -#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc - -/* Define TM6010 Audio decoder registers */ -/* This core available only in TM6010 */ -#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 -#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 -#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 -#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 -#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 -#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 -#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 -#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 -#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 -#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 -#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a -#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b -#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c -#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d -#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e -#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f -#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 -#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 -#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 -#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 -#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 -#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 -#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 -#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 -#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 -#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 -#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a -#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b -#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e -#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f -#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 -#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 -#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 -#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 -#define TM6010_REQ08_R24_A_SER 0x08, 0x24 -#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 -#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 -#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 -#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 - -/* Define TM6010 Video ADC registers */ -#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 -#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 -#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 -#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 -#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 -#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 -#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 -#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 -#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 -#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 -#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea -#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb -#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec -#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed - -/* Define TM6010 Audio ADC registers */ -#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 -#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 -#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 -#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/media/usb/tm6000/tm6000-stds.c b/drivers/media/usb/tm6000/tm6000-stds.c deleted file mode 100644 index 858cb4f3a9ca..000000000000 --- a/drivers/media/usb/tm6000/tm6000-stds.c +++ /dev/null @@ -1,623 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices -// -// Copyright (c) 2007 Mauro Carvalho Chehab - -#include -#include -#include "tm6000.h" -#include "tm6000-regs.h" - -static unsigned int tm6010_a_mode; -module_param(tm6010_a_mode, int, 0644); -MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); - -struct tm6000_reg_settings { - unsigned char req; - unsigned char reg; - unsigned char value; -}; - - -struct tm6000_std_settings { - v4l2_std_id id; - struct tm6000_reg_settings *common; -}; - -static struct tm6000_reg_settings composite_pal_m[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_pal_nc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_pal[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_secam[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings composite_ntsc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_std_settings composite_stds[] = { - { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, - { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, - { .id = V4L2_STD_PAL, .common = composite_pal, }, - { .id = V4L2_STD_SECAM, .common = composite_secam, }, - { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, -}; - -static struct tm6000_reg_settings svideo_pal_m[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_pal_nc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_pal[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_secam[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_reg_settings svideo_ntsc[] = { - { TM6010_REQ07_R3F_RESET, 0x01 }, - { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, - { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, - { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, - { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, - { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, - { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, - { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, - { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, - { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, - { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, - { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, - { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, - { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, - { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, - { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, - { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, - { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, - { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, - { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, - { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, - { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, - { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, - { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, - { TM6010_REQ07_R3F_RESET, 0x00 }, - { 0, 0, 0 } -}; - -static struct tm6000_std_settings svideo_stds[] = { - { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, - { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, - { .id = V4L2_STD_PAL, .common = svideo_pal, }, - { .id = V4L2_STD_SECAM, .common = svideo_secam, }, - { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, -}; - -static int tm6000_set_audio_std(struct tm6000_core *dev) -{ - uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ - uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ - uint8_t areg_06 = 0x02; /* Auto de-emphasis, manual channel mode */ - - if (dev->radio) { - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); - /* set mono or stereo */ - if (dev->amode == V4L2_TUNER_MODE_MONO) - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); - else if (dev->amode == V4L2_TUNER_MODE_STEREO) - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); - tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); - tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); - tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); - return 0; - } - - /* - * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one - * audio standard for each V4L2_STD type. - */ - if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) { - areg_05 |= 0x04; - } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) { - areg_05 |= 0x43; - } else if (dev->norm & V4L2_STD_MN) { - areg_05 |= 0x22; - } else switch (tm6010_a_mode) { - /* auto */ - case 0: - if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L) - areg_05 |= 0x00; - else /* Other PAL/SECAM standards */ - areg_05 |= 0x10; - break; - /* A2 */ - case 1: - if (dev->norm & V4L2_STD_DK) - areg_05 = 0x09; - else - areg_05 = 0x05; - break; - /* NICAM */ - case 2: - if (dev->norm & V4L2_STD_DK) { - areg_05 = 0x06; - } else if (dev->norm & V4L2_STD_PAL_I) { - areg_05 = 0x08; - } else if (dev->norm & V4L2_STD_SECAM_L) { - areg_05 = 0x0a; - areg_02 = 0x02; - } else { - areg_05 = 0x07; - } - break; - /* other */ - case 3: - if (dev->norm & V4L2_STD_DK) { - areg_05 = 0x0b; - } else { - areg_05 = 0x02; - } - break; - } - - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); - tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); - tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); - tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); - tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); - tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); - tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); - tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); - tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); - tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); - tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); - tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); - tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); - tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); - tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); - tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); - tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); - tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); - tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); - tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); - tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); - tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); - tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); - - return 0; -} - -void tm6000_get_std_res(struct tm6000_core *dev) -{ - /* Currently, those are the only supported resoltions */ - if (dev->norm & V4L2_STD_525_60) - dev->height = 480; - else - dev->height = 576; - - dev->width = 720; -} - -static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) -{ - int i, rc; - - /* Load board's initialization table */ - for (i = 0; set[i].req; i++) { - rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); - if (rc < 0) { - printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", - rc, set[i].req, set[i].reg, set[i].value); - return rc; - } - } - - return 0; -} - -int tm6000_set_standard(struct tm6000_core *dev) -{ - struct tm6000_input *input; - int i, rc = 0; - u8 reg_07_fe = 0x8a; - u8 reg_08_f1 = 0xfc; - u8 reg_08_e2 = 0xf0; - u8 reg_08_e6 = 0x0f; - - tm6000_get_std_res(dev); - - if (!dev->radio) - input = &dev->vinput[dev->input]; - else - input = &dev->rinput; - - if (dev->dev_type == TM6010) { - switch (input->vmux) { - case TM6000_VMUX_VIDEO_A: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); - reg_07_fe |= 0x01; - break; - case TM6000_VMUX_VIDEO_B: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); - reg_07_fe |= 0x01; - break; - case TM6000_VMUX_VIDEO_AB: - tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); - reg_08_e6 = 0x00; - tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); - tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); - tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); - tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); - break; - default: - break; - } - switch (input->amux) { - case TM6000_AMUX_ADC1: - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x00, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x10, 0xf0); - break; - case TM6000_AMUX_ADC2: - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x08, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x10, 0xf0); - break; - case TM6000_AMUX_SIF1: - reg_08_e2 |= 0x02; - reg_08_e6 = 0x08; - reg_07_fe |= 0x40; - reg_08_f1 |= 0x02; - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x02, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x30, 0xf0); - break; - case TM6000_AMUX_SIF2: - reg_08_e2 |= 0x02; - reg_08_e6 = 0x08; - reg_07_fe |= 0x40; - reg_08_f1 |= 0x02; - tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); - tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, - 0x02, 0x0f); - /* Mux overflow workaround */ - tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, - 0x30, 0xf0); - break; - default: - break; - } - tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); - tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); - tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); - tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); - } else { - switch (input->vmux) { - case TM6000_VMUX_VIDEO_A: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); - break; - case TM6000_VMUX_VIDEO_B: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); - break; - case TM6000_VMUX_VIDEO_AB: - tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); - tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); - tm6000_set_reg(dev, - REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); - break; - default: - break; - } - switch (input->amux) { - case TM6000_AMUX_ADC1: - tm6000_set_reg_mask(dev, - TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); - break; - case TM6000_AMUX_ADC2: - tm6000_set_reg_mask(dev, - TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); - break; - default: - break; - } - } - if (input->type == TM6000_INPUT_SVIDEO) { - for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { - if (dev->norm & svideo_stds[i].id) { - rc = tm6000_load_std(dev, svideo_stds[i].common); - goto ret; - } - } - return -EINVAL; - } else { - for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { - if (dev->norm & composite_stds[i].id) { - rc = tm6000_load_std(dev, composite_stds[i].common); - goto ret; - } - } - return -EINVAL; - } - -ret: - if (rc < 0) - return rc; - - if ((dev->dev_type == TM6010) && - ((input->amux == TM6000_AMUX_SIF1) || - (input->amux == TM6000_AMUX_SIF2))) - tm6000_set_audio_std(dev); - - msleep(40); - - return 0; -} diff --git a/drivers/media/usb/tm6000/tm6000-usb-isoc.h b/drivers/media/usb/tm6000/tm6000-usb-isoc.h deleted file mode 100644 index e3c6933f854d..000000000000 --- a/drivers/media/usb/tm6000/tm6000-usb-isoc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (c) 2006-2007 Mauro Carvalho Chehab - */ - -#include - -#define TM6000_URB_MSG_LEN 180 - -struct usb_isoc_ctl { - /* max packet size of isoc transaction */ - int max_pkt_size; - - /* number of allocated urbs */ - int num_bufs; - - /* urb for isoc transfers */ - struct urb **urb; - - /* transfer buffers for isoc transfer */ - char **transfer_buffer; - - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int vfield, field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - - /* Stores already requested buffers */ - struct tm6000_buffer *buf; -}; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c deleted file mode 100644 index d855a19551f3..000000000000 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ /dev/null @@ -1,1705 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices -// -// Copyright (c) 2006-2007 Mauro Carvalho Chehab -// -// Copyright (c) 2007 Michel Ludwig -// - Fixed module load/unload - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tm6000-regs.h" -#include "tm6000.h" - -#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ - -/* Limits minimum and default number of buffers */ -#define TM6000_MIN_BUF 4 -#define TM6000_DEF_BUF 8 - -#define TM6000_NUM_URB_BUF 8 - -#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ - -/* Declare static vars that will be used as parameters */ -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ -static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ -static bool keep_urb; /* keep urb buffers allocated */ - -/* Debug level */ -int tm6000_debug; -EXPORT_SYMBOL_GPL(tm6000_debug); - -static struct tm6000_fmt format[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - }, { - .fourcc = V4L2_PIX_FMT_TM6000, - .depth = 16, - } -}; - -/* ------------------------------------------------------------------ - * DMA and thread functions - * ------------------------------------------------------------------ - */ - -#define norm_maxw(a) 720 -#define norm_maxh(a) 576 - -#define norm_minw(a) norm_maxw(a) -#define norm_minh(a) norm_maxh(a) - -/* - * video-buf generic routine to get the next available buffer - */ -static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, - struct tm6000_buffer **buf) -{ - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - - if (list_empty(&dma_q->active)) { - dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); - *buf = NULL; - return; - } - - *buf = list_entry(dma_q->active.next, - struct tm6000_buffer, vb.queue); -} - -/* - * Announces that a buffer were filled and request the next - */ -static inline void buffer_filled(struct tm6000_core *dev, - struct tm6000_dmaqueue *dma_q, - struct tm6000_buffer *buf) -{ - /* Advice that buffer was filled */ - dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - buf->vb.ts = ktime_get_ns(); - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -/* - * Identify the tm5600/6000 buffer header type and properly handles - */ -static int copy_streams(u8 *data, unsigned long len, - struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 *ptr = data, *endp = data+len; - unsigned long header = 0; - int rc = 0; - unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; - struct tm6000_buffer *vbuf = NULL; - char *voutp = NULL; - unsigned int linewidth; - - if (!dev->radio) { - /* get video buffer */ - get_next_buf(dma_q, &vbuf); - - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - - if (!voutp) - return 0; - } - - for (ptr = data; ptr < endp;) { - if (!dev->isoc_ctl.cmd) { - /* Header */ - if (dev->isoc_ctl.tmp_buf_len > 0) { - /* from last urb or packet */ - header = dev->isoc_ctl.tmp_buf; - if (4 - dev->isoc_ctl.tmp_buf_len > 0) { - memcpy((u8 *)&header + - dev->isoc_ctl.tmp_buf_len, - ptr, - 4 - dev->isoc_ctl.tmp_buf_len); - ptr += 4 - dev->isoc_ctl.tmp_buf_len; - } - dev->isoc_ctl.tmp_buf_len = 0; - } else { - if (ptr + 3 >= endp) { - /* have incomplete header */ - dev->isoc_ctl.tmp_buf_len = endp - ptr; - memcpy(&dev->isoc_ctl.tmp_buf, ptr, - dev->isoc_ctl.tmp_buf_len); - return rc; - } - /* Seek for sync */ - for (; ptr < endp - 3; ptr++) { - if (*(ptr + 3) == 0x47) - break; - } - /* Get message header */ - header = *(unsigned long *)ptr; - ptr += 4; - } - - /* split the header fields */ - size = ((header & 0x7e) << 1); - if (size > 0) - size -= 4; - block = (header >> 7) & 0xf; - field = (header >> 11) & 0x1; - line = (header >> 12) & 0x1ff; - cmd = (header >> 21) & 0x7; - /* Validates header fields */ - if (size > TM6000_URB_MSG_LEN) - size = TM6000_URB_MSG_LEN; - pktsize = TM6000_URB_MSG_LEN; - /* - * calculate position in buffer and change the buffer - */ - switch (cmd) { - case TM6000_URB_MSG_VIDEO: - if (!dev->radio) { - if ((dev->isoc_ctl.vfield != field) && - (field == 1)) { - /* - * Announces that a new buffer - * were filled - */ - buffer_filled(dev, dma_q, vbuf); - dprintk(dev, V4L2_DEBUG_ISOC, - "new buffer filled\n"); - get_next_buf(dma_q, &vbuf); - if (!vbuf) - return rc; - voutp = videobuf_to_vmalloc(&vbuf->vb); - if (!voutp) - return rc; - memset(voutp, 0, vbuf->vb.size); - } - linewidth = vbuf->vb.width << 1; - pos = ((line << 1) - field - 1) * - linewidth + block * TM6000_URB_MSG_LEN; - /* Don't allow to write out of the buffer */ - if (pos + size > vbuf->vb.size) - cmd = TM6000_URB_MSG_ERR; - dev->isoc_ctl.vfield = field; - } - break; - case TM6000_URB_MSG_VBI: - break; - case TM6000_URB_MSG_AUDIO: - case TM6000_URB_MSG_PTS: - size = pktsize; /* Size is always 180 bytes */ - break; - } - } else { - /* Continue the last copy */ - cmd = dev->isoc_ctl.cmd; - size = dev->isoc_ctl.size; - pos = dev->isoc_ctl.pos; - pktsize = dev->isoc_ctl.pktsize; - field = dev->isoc_ctl.field; - } - cpysize = (endp - ptr > size) ? size : endp - ptr; - if (cpysize) { - /* copy data in different buffers */ - switch (cmd) { - case TM6000_URB_MSG_VIDEO: - /* Fills video buffer */ - if (vbuf) - memcpy(&voutp[pos], ptr, cpysize); - break; - case TM6000_URB_MSG_AUDIO: { - int i; - for (i = 0; i < cpysize; i += 2) - swab16s((u16 *)(ptr + i)); - - tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); - break; - } - case TM6000_URB_MSG_VBI: - /* Need some code to copy vbi buffer */ - break; - case TM6000_URB_MSG_PTS: { - /* Need some code to copy pts */ - u32 pts; - pts = *(u32 *)ptr; - dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", - field, pts); - break; - } - } - } - if (ptr + pktsize > endp) { - /* - * End of URB packet, but cmd processing is not - * complete. Preserve the state for a next packet - */ - dev->isoc_ctl.pos = pos + cpysize; - dev->isoc_ctl.size = size - cpysize; - dev->isoc_ctl.cmd = cmd; - dev->isoc_ctl.field = field; - dev->isoc_ctl.pktsize = pktsize - (endp - ptr); - ptr += endp - ptr; - } else { - dev->isoc_ctl.cmd = 0; - ptr += pktsize; - } - } - return 0; -} - -/* - * Identify the tm5600/6000 buffer header type and properly handles - */ -static int copy_multiplexed(u8 *ptr, unsigned long len, - struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - unsigned int pos = dev->isoc_ctl.pos, cpysize; - int rc = 1; - struct tm6000_buffer *buf; - char *outp = NULL; - - get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); - - if (!outp) - return 0; - - while (len > 0) { - cpysize = min(len, buf->vb.size-pos); - memcpy(&outp[pos], ptr, cpysize); - pos += cpysize; - ptr += cpysize; - len -= cpysize; - if (pos >= buf->vb.size) { - pos = 0; - /* Announces that a new buffer were filled */ - buffer_filled(dev, dma_q, buf); - dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); - get_next_buf(dma_q, &buf); - if (!buf) - break; - outp = videobuf_to_vmalloc(&(buf->vb)); - if (!outp) - return rc; - pos = 0; - } - } - - dev->isoc_ctl.pos = pos; - return rc; -} - -static inline void print_err_status(struct tm6000_core *dev, - int packet, int status) -{ - char *errmsg = "Unknown"; - - switch (status) { - case -ENOENT: - errmsg = "unlinked synchronously"; - break; - case -ECONNRESET: - errmsg = "unlinked asynchronously"; - break; - case -ENOSR: - errmsg = "Buffer error (overrun)"; - break; - case -EPIPE: - errmsg = "Stalled (device not responding)"; - break; - case -EOVERFLOW: - errmsg = "Babble (bad cable?)"; - break; - case -EPROTO: - errmsg = "Bit-stuff error (bad cable?)"; - break; - case -EILSEQ: - errmsg = "CRC/Timeout (could be anything)"; - break; - case -ETIME: - errmsg = "Device does not respond"; - break; - } - if (packet < 0) { - dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", - status, errmsg); - } else { - dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", - packet, status, errmsg); - } -} - - -/* - * Controls the isoc copy of each urb packet - */ -static inline int tm6000_isoc_copy(struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - int i, len = 0, rc = 1, status; - char *p; - - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - return 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - status = urb->iso_frame_desc[i].status; - - if (status < 0) { - print_err_status(dev, i, status); - continue; - } - - len = urb->iso_frame_desc[i].actual_length; - - if (len > 0) { - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (!urb->iso_frame_desc[i].status) { - if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { - rc = copy_multiplexed(p, len, urb); - if (rc <= 0) - return rc; - } else { - copy_streams(p, len, urb); - } - } - } - } - return rc; -} - -/* ------------------------------------------------------------------ - * URB control - * ------------------------------------------------------------------ - */ - -/* - * IRQ callback, called by URB callback - */ -static void tm6000_irq_callback(struct urb *urb) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - unsigned long flags; - int i; - - switch (urb->status) { - case 0: - case -ETIMEDOUT: - break; - - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - return; - - default: - tm6000_err("urb completion error %d.\n", urb->status); - break; - } - - spin_lock_irqsave(&dev->slock, flags); - tm6000_isoc_copy(urb); - spin_unlock_irqrestore(&dev->slock, flags); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) - tm6000_err("urb resubmit failed (error=%i)\n", - urb->status); -} - -/* - * Allocate URB buffers - */ -static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) -{ - int num_bufs = TM6000_NUM_URB_BUF; - int i; - - if (dev->urb_buffer) - return 0; - - dev->urb_buffer = kmalloc_array(num_bufs, sizeof(*dev->urb_buffer), - GFP_KERNEL); - if (!dev->urb_buffer) - return -ENOMEM; - - dev->urb_dma = kmalloc_array(num_bufs, sizeof(*dev->urb_dma), - GFP_KERNEL); - if (!dev->urb_dma) - return -ENOMEM; - - for (i = 0; i < num_bufs; i++) { - dev->urb_buffer[i] = usb_alloc_coherent( - dev->udev, dev->urb_size, - GFP_KERNEL, &dev->urb_dma[i]); - if (!dev->urb_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", - dev->urb_size, i); - return -ENOMEM; - } - memset(dev->urb_buffer[i], 0, dev->urb_size); - } - - return 0; -} - -/* - * Free URB buffers - */ -static int tm6000_free_urb_buffers(struct tm6000_core *dev) -{ - int i; - - if (!dev->urb_buffer) - return 0; - - for (i = 0; i < TM6000_NUM_URB_BUF; i++) { - if (dev->urb_buffer[i]) { - usb_free_coherent(dev->udev, - dev->urb_size, - dev->urb_buffer[i], - dev->urb_dma[i]); - dev->urb_buffer[i] = NULL; - } - } - kfree(dev->urb_buffer); - kfree(dev->urb_dma); - dev->urb_buffer = NULL; - dev->urb_dma = NULL; - - return 0; -} - -/* - * Stop and Deallocate URBs - */ -static void tm6000_uninit_isoc(struct tm6000_core *dev) -{ - struct urb *urb; - int i; - - dev->isoc_ctl.buf = NULL; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; - if (urb) { - usb_kill_urb(urb); - usb_unlink_urb(urb); - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - if (!keep_urb) - tm6000_free_urb_buffers(dev); - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; -} - -/* - * Assign URBs and start IRQ - */ -static int tm6000_prepare_isoc(struct tm6000_core *dev) -{ - struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets; - int num_bufs = TM6000_NUM_URB_BUF; - struct urb *urb; - - /* De-allocates all pending stuff */ - tm6000_uninit_isoc(dev); - /* Stop interrupt USB pipe */ - tm6000_ir_int_stop(dev); - - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, - dev->isoc_in.bAlternateSetting); - - /* Start interrupt USB pipe */ - tm6000_ir_int_start(dev); - - pipe = usb_rcvisocpipe(dev->udev, - dev->isoc_in.endp->desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK); - - size = usb_maxpacket(dev->udev, pipe); - - if (size > dev->isoc_in.maxsize) - size = dev->isoc_in.maxsize; - - dev->isoc_ctl.max_pkt_size = size; - - max_packets = TM6000_MAX_ISO_PACKETS; - sb_size = max_packets * size; - dev->urb_size = sb_size; - - dev->isoc_ctl.num_bufs = num_bufs; - - dev->isoc_ctl.urb = kmalloc_array(num_bufs, sizeof(void *), - GFP_KERNEL); - if (!dev->isoc_ctl.urb) - return -ENOMEM; - - dev->isoc_ctl.transfer_buffer = kmalloc_array(num_bufs, - sizeof(void *), - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets (%d bytes) of %d bytes each to handle %u size\n", - max_packets, num_bufs, sb_size, - dev->isoc_in.maxsize, size); - - - if (tm6000_alloc_urb_buffers(dev) < 0) { - tm6000_err("cannot allocate memory for urb buffers\n"); - - /* call free, as some buffers might have been allocated */ - tm6000_free_urb_buffers(dev); - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - return -ENOMEM; - } - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - tm6000_uninit_isoc(dev); - tm6000_free_urb_buffers(dev); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - - urb->transfer_dma = dev->urb_dma[i]; - dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; - - usb_fill_bulk_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, - tm6000_irq_callback, dma_q); - urb->interval = dev->isoc_in.endp->desc.bInterval; - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = size * j; - urb->iso_frame_desc[j].length = size; - } - } - - return 0; -} - -static int tm6000_start_thread(struct tm6000_core *dev) -{ - struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i; - - dma_q->frame = 0; - dma_q->ini_jiffies = jiffies; - - init_waitqueue_head(&dma_q->wq); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); - if (rc) { - tm6000_err("submit of urb %i failed (error=%i)\n", i, - rc); - tm6000_uninit_isoc(dev); - return rc; - } - } - - return 0; -} - -/* ------------------------------------------------------------------ - * Videobuf operations - * ------------------------------------------------------------------ - */ - -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) -{ - struct tm6000_fh *fh = vq->priv_data; - - *size = fh->fmt->depth * fh->width * fh->height >> 3; - if (0 == *count) - *count = TM6000_DEF_BUF; - - if (*count < TM6000_MIN_BUF) - *count = TM6000_MIN_BUF; - - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) -{ - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_core *dev = fh->dev; - unsigned long flags; - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - struct tm6000_core *dev = fh->dev; - int rc = 0; - - BUG_ON(NULL == fh->fmt); - - - /* FIXME: It assumes depth=2 */ - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - buf->vb.state = VIDEOBUF_NEEDS_INIT; - } - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc != 0) - goto fail; - } - - if (!dev->isoc_ctl.num_bufs) { - rc = tm6000_prepare_isoc(dev); - if (rc < 0) - goto fail; - - rc = tm6000_start_thread(dev); - if (rc < 0) - goto fail; - - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - struct tm6000_fh *fh = vq->priv_data; - struct tm6000_core *dev = fh->dev; - struct tm6000_dmaqueue *vidq = &dev->vidq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); - - free_buffer(vq, buf); -} - -static const struct videobuf_queue_ops tm6000_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* ------------------------------------------------------------------ - * IOCTL handling - * ------------------------------------------------------------------ - */ - -static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh && dev->is_res_read) - return true; - - return false; -} - -static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh) - return true; - - return false; -} - -static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, - bool is_res_read) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources == fh && dev->is_res_read == is_res_read) - return true; - - /* is it free? */ - if (dev->resources) - return false; - - /* grab it */ - dev->resources = fh; - dev->is_res_read = is_res_read; - dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); - return true; -} - -static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) -{ - /* Is the current fh handling it? if so, that's OK */ - if (dev->resources != fh) - return; - - dev->resources = NULL; - dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); -} - -/* ------------------------------------------------------------------ - * IOCTL vidioc handling - * ------------------------------------------------------------------ - */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - - strscpy(cap->driver, "tm6000", sizeof(cap->driver)); - strscpy(cap->card, "Trident TM5600/6000/6010", sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_DEVICE_CAPS; - if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; - if (dev->caps.has_radio) - cap->capabilities |= V4L2_CAP_RADIO; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(format)) - return -EINVAL; - - f->pixelformat = format[f->index].fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_fh *fh = priv; - - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - - return 0; -} - -static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(format); i++) - if (format[i].fourcc == fourcc) - return format+i; - return NULL; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - struct tm6000_fmt *fmt; - enum v4l2_field field; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) { - dprintk(dev, 2, "Fourcc format (0x%08x) invalid.\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - field = f->fmt.pix.field; - - field = V4L2_FIELD_INTERLACED; - - tm6000_get_std_res(dev); - - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - - f->fmt.pix.width &= ~0x01; - - f->fmt.pix.field = field; - - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -/*FIXME: This seems to be generic enough to be at videodev2 */ -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int ret = vidioc_try_fmt_vid_cap(file, fh, f); - if (ret < 0) - return ret; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - - dev->fourcc = f->fmt.pix.pixelformat; - - tm6000_set_fourcc_format(dev); - - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_reqbufs(&fh->vb_vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_querybuf(&fh->vb_vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_qbuf(&fh->vb_vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct tm6000_fh *fh = priv; - - return videobuf_dqbuf(&fh->vb_vidq, p, - file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - if (!res_get(dev, fh, false)) - return -EBUSY; - return videobuf_streamon(&fh->vb_vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (i != fh->type) - return -EINVAL; - - videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); - - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) -{ - int rc = 0; - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - dev->norm = norm; - rc = tm6000_init_analog_mode(dev); - - fh->width = dev->width; - fh->height = dev->height; - - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); - - return 0; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - *norm = dev->norm; - return 0; -} - -static const char *iname[] = { - [TM6000_INPUT_TV] = "Television", - [TM6000_INPUT_COMPOSITE1] = "Composite 1", - [TM6000_INPUT_COMPOSITE2] = "Composite 2", - [TM6000_INPUT_SVIDEO] = "S-Video", -}; - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - unsigned int n; - - n = i->index; - if (n >= 3) - return -EINVAL; - - if (!dev->vinput[n].type) - return -EINVAL; - - i->index = n; - - if (dev->vinput[n].type == TM6000_INPUT_TV) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; - - strscpy(i->name, iname[dev->vinput[n].type], sizeof(i->name)); - - i->std = TM6000_STD; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - *i = dev->input; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int rc = 0; - - if (i >= 3) - return -EINVAL; - if (!dev->vinput[i].type) - return -EINVAL; - - dev->input = i; - - rc = vidioc_s_std(file, priv, dev->norm); - - return rc; -} - -/* --- controls ---------------------------------------------- */ - -static int tm6000_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler); - u8 val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); - return 0; - case V4L2_CID_BRIGHTNESS: - tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); - return 0; - case V4L2_CID_SATURATION: - tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); - return 0; - case V4L2_CID_HUE: - tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); - return 0; - } - return -EINVAL; -} - -static const struct v4l2_ctrl_ops tm6000_ctrl_ops = { - .s_ctrl = tm6000_s_ctrl, -}; - -static int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct tm6000_core *dev = container_of(ctrl->handler, - struct tm6000_core, radio_ctrl_handler); - u8 val = ctrl->val; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->ctl_mute = val; - tm6000_tvaudio_set_mute(dev, val); - return 0; - case V4L2_CID_AUDIO_VOLUME: - dev->ctl_volume = val; - tm6000_set_volume(dev, val); - return 0; - } - return -EINVAL; -} - -static const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = { - .s_ctrl = tm6000_radio_s_ctrl, -}; - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (UNSET == dev->tuner_type) - return -ENOTTY; - if (0 != t->index) - return -EINVAL; - - strscpy(t->name, "Television", sizeof(t->name)); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; - t->rangehigh = 0xffffffffUL; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - t->audmode = dev->amode; - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (UNSET == dev->tuner_type) - return -ENOTTY; - if (0 != t->index) - return -EINVAL; - - if (t->audmode > V4L2_TUNER_MODE_STEREO) - dev->amode = V4L2_TUNER_MODE_STEREO; - else - dev->amode = t->audmode; - dprintk(dev, 3, "audio mode: %x\n", t->audmode); - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (UNSET == dev->tuner_type) - return -ENOTTY; - if (f->tuner) - return -EINVAL; - - f->frequency = dev->freq; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); - - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - const struct v4l2_frequency *f) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (UNSET == dev->tuner_type) - return -ENOTTY; - if (f->tuner != 0) - return -EINVAL; - - dev->freq = f->frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); - - return 0; -} - -static int radio_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (0 != t->index) - return -EINVAL; - - memset(t, 0, sizeof(*t)); - strscpy(t->name, "Radio", sizeof(t->name)); - t->type = V4L2_TUNER_RADIO; - t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - t->audmode = V4L2_TUNER_MODE_STEREO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - return 0; -} - -static int radio_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *t) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (0 != t->index) - return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - return 0; -} - -/* ------------------------------------------------------------------ - File operations for the device - ------------------------------------------------------------------*/ - -static int __tm6000_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct tm6000_core *dev = video_drvdata(file); - struct tm6000_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int rc; - int radio = 0; - - dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", - video_device_node_name(vdev)); - - switch (vdev->vfl_type) { - case VFL_TYPE_VIDEO: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - default: - return -EINVAL; - } - - /* If more than one user, mutex should be added */ - dev->users++; - - dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[type], - dev->users); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users--; - return -ENOMEM; - } - - v4l2_fh_init(&fh->fh, vdev); - file->private_data = fh; - fh->dev = dev; - fh->radio = radio; - dev->radio = radio; - fh->type = type; - dev->fourcc = format[0].fourcc; - - fh->fmt = format_by_fourcc(dev->fourcc); - - tm6000_get_std_res(dev); - - fh->width = dev->width; - fh->height = dev->height; - - dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n", - fh, dev, &dev->vidq); - dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n", - list_empty(&dev->vidq.queued)); - dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n", - list_empty(&dev->vidq.active)); - - /* initialize hardware on analog mode */ - rc = tm6000_init_analog_mode(dev); - if (rc < 0) { - v4l2_fh_exit(&fh->fh); - kfree(fh); - return rc; - } - - dev->mode = TM6000_MODE_ANALOG; - - if (!fh->radio) { - videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, - NULL, &dev->slock, - fh->type, - V4L2_FIELD_INTERLACED, - sizeof(struct tm6000_buffer), fh, &dev->lock); - } else { - dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); - tm6000_set_audio_rinput(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); - tm6000_prepare_isoc(dev); - tm6000_start_thread(dev); - } - v4l2_fh_add(&fh->fh); - - return 0; -} - -static int tm6000_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - int res; - - mutex_lock(vdev->lock); - res = __tm6000_open(file); - mutex_unlock(vdev->lock); - return res; -} - -static ssize_t -tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - int res; - - if (!res_get(fh->dev, fh, true)) - return -EBUSY; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, - file->f_flags & O_NONBLOCK); - mutex_unlock(&dev->lock); - return res; - } - return 0; -} - -static __poll_t -__tm6000_poll(struct file *file, struct poll_table_struct *wait) -{ - __poll_t req_events = poll_requested_events(wait); - struct tm6000_fh *fh = file->private_data; - struct tm6000_buffer *buf; - __poll_t res = 0; - - if (v4l2_event_pending(&fh->fh)) - res = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->fh.wait, wait); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return res | EPOLLERR; - - if (!!is_res_streaming(fh->dev, fh)) - return res | EPOLLERR; - - if (!is_res_read(fh->dev, fh)) { - /* streaming capture */ - if (list_empty(&fh->vb_vidq.stream)) - return res | EPOLLERR; - buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return res | EPOLLIN | EPOLLRDNORM; - } else if (req_events & (EPOLLIN | EPOLLRDNORM)) { - /* read() capture */ - return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); - } - return res; -} - -static __poll_t tm6000_poll(struct file *file, struct poll_table_struct *wait) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - __poll_t res; - - mutex_lock(&dev->lock); - res = __tm6000_poll(file, wait); - mutex_unlock(&dev->lock); - return res; -} - -static int tm6000_release(struct file *file) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - struct video_device *vdev = video_devdata(file); - - dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users); - - mutex_lock(&dev->lock); - dev->users--; - - res_free(dev, fh); - - if (!dev->users) { - tm6000_uninit_isoc(dev); - - /* Stop interrupt USB pipe */ - tm6000_ir_int_stop(dev); - - usb_reset_configuration(dev->udev); - - if (dev->int_in.endp) - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, 2); - else - usb_set_interface(dev->udev, - dev->isoc_in.bInterfaceNumber, 0); - - /* Start interrupt USB pipe */ - tm6000_ir_int_start(dev); - - if (!fh->radio) - videobuf_mmap_free(&fh->vb_vidq); - } - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh); - mutex_unlock(&dev->lock); - - return 0; -} - -static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - int res; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - res = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - return res; -} - -static const struct v4l2_file_operations tm6000_fops = { - .owner = THIS_MODULE, - .open = tm6000_open, - .release = tm6000_release, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .read = tm6000_read, - .poll = tm6000_poll, - .mmap = tm6000_mmap, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device tm6000_template = { - .name = "tm6000", - .fops = &tm6000_fops, - .ioctl_ops = &video_ioctl_ops, - .release = video_device_release_empty, - .tvnorms = TM6000_STD, -}; - -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = tm6000_open, - .poll = v4l2_ctrl_poll, - .release = tm6000_release, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = radio_g_tuner, - .vidioc_s_tuner = radio_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device tm6000_radio_template = { - .name = "tm6000", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, -}; - -/* ----------------------------------------------------------------- - * Initialization and module stuff - * ------------------------------------------------------------------ - */ - -static void vdev_init(struct tm6000_core *dev, - struct video_device *vfd, - const struct video_device - *template, const char *type_name) -{ - *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release_empty; - vfd->lock = &dev->lock; - - snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); - - video_set_drvdata(vfd, dev); -} - -int tm6000_v4l2_register(struct tm6000_core *dev) -{ - int ret = 0; - - v4l2_ctrl_handler_init(&dev->ctrl_handler, 6); - v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2); - v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); - v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); - v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 54); - v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 119); - v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 112); - v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - v4l2_ctrl_add_handler(&dev->ctrl_handler, - &dev->radio_ctrl_handler, NULL, false); - - if (dev->radio_ctrl_handler.error) - ret = dev->radio_ctrl_handler.error; - if (!ret && dev->ctrl_handler.error) - ret = dev->ctrl_handler.error; - if (ret) - goto free_ctrl; - - vdev_init(dev, &dev->vfd, &tm6000_template, "video"); - - dev->vfd.ctrl_handler = &dev->ctrl_handler; - dev->vfd.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - if (dev->tuner_type != TUNER_ABSENT) - dev->vfd.device_caps |= V4L2_CAP_TUNER; - - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - - ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr); - - if (ret < 0) { - printk(KERN_INFO "%s: can't register video device\n", - dev->name); - goto free_ctrl; - } - - printk(KERN_INFO "%s: registered device %s\n", - dev->name, video_device_node_name(&dev->vfd)); - - if (dev->caps.has_radio) { - vdev_init(dev, &dev->radio_dev, &tm6000_radio_template, - "radio"); - dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler; - dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; - ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO, - radio_nr); - if (ret < 0) { - printk(KERN_INFO "%s: can't register radio device\n", - dev->name); - goto unreg_video; - } - - printk(KERN_INFO "%s: registered device %s\n", - dev->name, video_device_node_name(&dev->radio_dev)); - } - - printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); - return ret; - -unreg_video: - video_unregister_device(&dev->vfd); -free_ctrl: - v4l2_ctrl_handler_free(&dev->ctrl_handler); - v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); - return ret; -} - -int tm6000_v4l2_unregister(struct tm6000_core *dev) -{ - video_unregister_device(&dev->vfd); - - /* if URB buffers are still allocated free them now */ - tm6000_free_urb_buffers(dev); - - video_unregister_device(&dev->radio_dev); - return 0; -} - -int tm6000_v4l2_exit(void) -{ - return 0; -} - -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, "Allow changing video device number"); - -module_param_named(debug, tm6000_debug, int, 0444); -MODULE_PARM_DESC(debug, "activates debug info"); - -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - -module_param(keep_urb, bool, 0); -MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h deleted file mode 100644 index c08c95312739..000000000000 --- a/drivers/media/usb/tm6000/tm6000.h +++ /dev/null @@ -1,396 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices - * - * Copyright (c) 2006-2007 Mauro Carvalho Chehab - * - * Copyright (c) 2007 Michel Ludwig - * - DVB-T support - */ - -#include -#include -#include -#include "tm6000-usb-isoc.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Inputs */ -enum tm6000_itype { - TM6000_INPUT_TV = 1, - TM6000_INPUT_COMPOSITE1, - TM6000_INPUT_COMPOSITE2, - TM6000_INPUT_SVIDEO, - TM6000_INPUT_DVB, - TM6000_INPUT_RADIO, -}; - -enum tm6000_mux { - TM6000_VMUX_VIDEO_A = 1, - TM6000_VMUX_VIDEO_B, - TM6000_VMUX_VIDEO_AB, - TM6000_AMUX_ADC1, - TM6000_AMUX_ADC2, - TM6000_AMUX_SIF1, - TM6000_AMUX_SIF2, - TM6000_AMUX_I2S, -}; - -enum tm6000_devtype { - TM6000 = 0, - TM5600, - TM6010, -}; - -struct tm6000_input { - enum tm6000_itype type; - enum tm6000_mux vmux; - enum tm6000_mux amux; - unsigned int v_gpio; - unsigned int a_gpio; -}; - -/* ------------------------------------------------------------------ - * Basic structures - * ------------------------------------------------------------------ - */ - -struct tm6000_fmt { - u32 fourcc; /* v4l2 format id */ - int depth; -}; - -/* buffer for one video frame */ -struct tm6000_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - struct tm6000_fmt *fmt; -}; - -struct tm6000_dmaqueue { - struct list_head active; - struct list_head queued; - - /* thread for generating video stream*/ - struct task_struct *kthread; - wait_queue_head_t wq; - /* Counters to control fps rate */ - int frame; - int ini_jiffies; -}; - -/* device states */ -enum tm6000_core_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -/* io methods */ -enum tm6000_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum tm6000_mode { - TM6000_MODE_UNKNOWN = 0, - TM6000_MODE_ANALOG, - TM6000_MODE_DIGITAL, -}; - -struct tm6000_gpio { - int tuner_reset; - int tuner_on; - int demod_reset; - int demod_on; - int power_led; - int dvb_led; - int ir; -}; - -struct tm6000_capabilities { - unsigned int has_tuner:1; - unsigned int has_tda9874:1; - unsigned int has_dvb:1; - unsigned int has_zl10353:1; - unsigned int has_eeprom:1; - unsigned int has_remote:1; - unsigned int has_radio:1; -}; - -struct tm6000_dvb { - struct dvb_adapter adapter; - struct dvb_demux demux; - struct dvb_frontend *frontend; - struct dmxdev dmxdev; - unsigned int streams; - struct urb *bulk_urb; - struct mutex mutex; -}; - -struct snd_tm6000_card { - struct snd_card *card; - spinlock_t reg_lock; - struct tm6000_core *core; - struct snd_pcm_substream *substream; - - /* temporary data for buffer fill processing */ - unsigned buf_pos; - unsigned period_pos; -}; - -struct tm6000_endpoint { - struct usb_host_endpoint *endp; - __u8 bInterfaceNumber; - __u8 bAlternateSetting; - unsigned maxsize; -}; - -#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) - -struct tm6000_core { - /* generic device properties */ - char name[30]; /* name (including minor) of the device */ - int model; /* index in the device_data struct */ - int devno; /* marks the number of this device */ - enum tm6000_devtype dev_type; /* type of device */ - unsigned char eedata[256]; /* Eeprom data */ - unsigned eedata_size; /* Size of the eeprom info */ - - v4l2_std_id norm; /* Current norm */ - int width, height; /* Selected resolution */ - - enum tm6000_core_state state; - - /* Device Capabilities*/ - struct tm6000_capabilities caps; - - /* Used to load alsa/dvb */ - struct work_struct request_module_wk; - - /* Tuner configuration */ - int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - - struct tm6000_gpio gpio; - - char *ir_codes; - - __u8 radio; - - /* Demodulator configuration */ - int demod_addr; /* demodulator address */ - - int audio_bitrate; - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; - - - /* extension */ - struct list_head devlist; - - /* video for linux */ - int users; - - /* various device info */ - struct tm6000_fh *resources; /* Points to fh that is streaming */ - bool is_res_read; - - struct video_device vfd; - struct video_device radio_dev; - struct tm6000_dmaqueue vidq; - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl_handler radio_ctrl_handler; - - int input; - struct tm6000_input vinput[3]; /* video input */ - struct tm6000_input rinput; /* radio input */ - - int freq; - unsigned int fourcc; - - enum tm6000_mode mode; - - int ctl_mute; /* audio */ - int ctl_volume; - int amode; - - /* DVB-T support */ - struct tm6000_dvb *dvb; - - /* audio support */ - struct snd_tm6000_card *adev; - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ - - struct tm6000_IR *ir; - - /* locks */ - struct mutex lock; - struct mutex usb_lock; - - /* usb transfer */ - struct usb_device *udev; /* the usb device */ - - struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; - struct tm6000_endpoint int_in, int_out; - - /* scaler!=0 if scaler is active*/ - int scaler; - - /* Isoc control struct */ - struct usb_isoc_ctl isoc_ctl; - - spinlock_t slock; - - /* urb dma buffers */ - char **urb_buffer; - dma_addr_t *urb_dma; - unsigned int urb_size; - - unsigned long quirks; -}; - -enum tm6000_ops_type { - TM6000_AUDIO = 0x10, - TM6000_DVB = 0x20, -}; - -struct tm6000_ops { - struct list_head next; - char *name; - enum tm6000_ops_type type; - int (*init)(struct tm6000_core *); - int (*fini)(struct tm6000_core *); - int (*fillbuf)(struct tm6000_core *, char *buf, int size); -}; - -struct tm6000_fh { - struct v4l2_fh fh; - struct tm6000_core *dev; - unsigned int radio; - - /* video capture */ - struct tm6000_fmt *fmt; - unsigned int width, height; - struct videobuf_queue vb_vidq; - - enum v4l2_buf_type type; -}; - -#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ - V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ - V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) - -/* In tm6000-cards.c */ - -int tm6000_tuner_callback(void *ptr, int component, int command, int arg); -int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); -int tm6000_cards_setup(struct tm6000_core *dev); -void tm6000_flash_led(struct tm6000_core *dev, u8 state); - -/* In tm6000-core.c */ - -int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, - u16 value, u16 index, u8 *buf, u16 len); -int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, - u16 index, u16 mask); -int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); -int tm6000_init(struct tm6000_core *dev); -int tm6000_reset(struct tm6000_core *dev); - -int tm6000_init_analog_mode(struct tm6000_core *dev); -int tm6000_init_digital_mode(struct tm6000_core *dev); -int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); -int tm6000_set_audio_rinput(struct tm6000_core *dev); -int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); -void tm6000_set_volume(struct tm6000_core *dev, int vol); - -int tm6000_v4l2_register(struct tm6000_core *dev); -int tm6000_v4l2_unregister(struct tm6000_core *dev); -int tm6000_v4l2_exit(void); -void tm6000_set_fourcc_format(struct tm6000_core *dev); - -void tm6000_remove_from_devlist(struct tm6000_core *dev); -void tm6000_add_into_devlist(struct tm6000_core *dev); -int tm6000_register_extension(struct tm6000_ops *ops); -void tm6000_unregister_extension(struct tm6000_ops *ops); -void tm6000_init_extension(struct tm6000_core *dev); -void tm6000_close_extension(struct tm6000_core *dev); -int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, - char *buf, int size); - - -/* In tm6000-stds.c */ -void tm6000_get_std_res(struct tm6000_core *dev); -int tm6000_set_standard(struct tm6000_core *dev); - -/* In tm6000-i2c.c */ -int tm6000_i2c_register(struct tm6000_core *dev); -int tm6000_i2c_unregister(struct tm6000_core *dev); - -/* In tm6000-queue.c */ - -int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); - -int tm6000_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i); -int tm6000_vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i); -int tm6000_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb); -int tm6000_vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b); -int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); -int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); -ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, - loff_t *f_pos); -unsigned int tm6000_v4l2_poll(struct file *file, - struct poll_table_struct *wait); -int tm6000_queue_init(struct tm6000_core *dev); - -/* In tm6000-alsa.c */ -/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ - -/* In tm6000-input.c */ -int tm6000_ir_init(struct tm6000_core *dev); -int tm6000_ir_fini(struct tm6000_core *dev); -void tm6000_ir_wait(struct tm6000_core *dev, u8 state); -int tm6000_ir_int_start(struct tm6000_core *dev); -void tm6000_ir_int_stop(struct tm6000_core *dev); - -/* Debug stuff */ - -extern int tm6000_debug; - -#define dprintk(dev, level, fmt, arg...) do {\ - if (tm6000_debug & level) \ - printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ - dev->name, __func__ , ##arg); } while (0) - -#define V4L2_DEBUG_REG 0x0004 -#define V4L2_DEBUG_I2C 0x0008 -#define V4L2_DEBUG_QUEUE 0x0010 -#define V4L2_DEBUG_ISOC 0x0020 -#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ -#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ - -#define tm6000_err(fmt, arg...) do {\ - printk(KERN_ERR "tm6000 %s :"fmt, \ - __func__ , ##arg); } while (0) diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 84f627ccf63e..0a0e052e39b1 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -58,6 +58,7 @@ if STAGING_MEDIA_DEPRECATED source "drivers/staging/media/deprecated/cpia2/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" +source "drivers/staging/media/deprecated/tm6000/Kconfig" source "drivers/staging/media/deprecated/zr364xx/Kconfig" endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index ed7e5a61fc00..b7df6302dd8e 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ obj-$(CONFIG_DVB_AV7110) += av7110/ diff --git a/drivers/staging/media/deprecated/tm6000/Kconfig b/drivers/staging/media/deprecated/tm6000/Kconfig new file mode 100644 index 000000000000..73d72e49eb28 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver (DEPRECATED)" + depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB + select VIDEO_TUNER + select MEDIA_TUNER_XC2028 + select MEDIA_TUNER_XC5000 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND + select SND_PCM + help + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + tristate "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && USB + select DVB_ZL10353 + help + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/staging/media/deprecated/tm6000/Makefile b/drivers/staging/media/deprecated/tm6000/Makefile new file mode 100644 index 000000000000..75247a02a485 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +tm6000-y := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o \ + tm6000-input.o + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o +obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o + +ccflags-y += -I $(srctree)/drivers/media/tuners +ccflags-y += -I $(srctree)/drivers/media/dvb-frontends diff --git a/drivers/staging/media/deprecated/tm6000/TODO b/drivers/staging/media/deprecated/tm6000/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c b/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c new file mode 100644 index 000000000000..a19a46770c2b --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +// Support for audio capture for tm5600/6000/6010 +// Copyright (c) 2007-2008 Mauro Carvalho Chehab +// +// Based on cx88-alsa.c + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ + +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL v2"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific functions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Starting audio DMA\n"); + + /* Enables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); + + tm6000_set_audio_bitrate(core, 48000); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Stopping audio DMA\n"); + + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static const struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, + .buffer_bytes_max = 62720 * 8, +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + return 0; +} + +static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) +{ + struct snd_tm6000_card *chip = core->adev; + struct snd_pcm_substream *substream = chip->substream; + struct snd_pcm_runtime *runtime; + int period_elapsed = 0; + unsigned int stride, buf_pos; + int length; + + if (atomic_read(&core->stream_started) == 0) + return 0; + + if (!size || !substream) { + dprintk(1, "substream was NULL\n"); + return -EINVAL; + } + + runtime = substream->runtime; + if (!runtime || !runtime->dma_area) { + dprintk(1, "runtime was NULL\n"); + return -EINVAL; + } + + buf_pos = chip->buf_pos; + stride = runtime->frame_bits >> 3; + + if (stride == 0) { + dprintk(1, "stride is zero\n"); + return -EINVAL; + } + + length = size / stride; + if (length == 0) { + dprintk(1, "%s: length was zero\n", __func__); + return -EINVAL; + } + + dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, + runtime->dma_area, buf_pos, + (unsigned int)runtime->buffer_size, stride); + + if (buf_pos + length >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - buf_pos; + memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); + memcpy(runtime->dma_area, buf + cnt * stride, + length * stride - cnt * stride); + } else + memcpy(runtime->dma_area + buf_pos * stride, buf, + length * stride); + + snd_pcm_stream_lock(substream); + + chip->buf_pos += length; + if (chip->buf_pos >= runtime->buffer_size) + chip->buf_pos -= runtime->buffer_size; + + chip->period_pos += length; + if (chip->period_pos >= runtime->period_size) { + chip->period_pos -= runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + chip->buf_pos = 0; + chip->period_pos = 0; + + return 0; +} + + +/* + * trigger callback + */ +static void audio_trigger(struct work_struct *work) +{ + struct tm6000_core *core = container_of(work, struct tm6000_core, + wq_trigger); + struct snd_tm6000_card *chip = core->adev; + + if (atomic_read(&core->stream_started)) { + dprintk(1, "starting capture"); + _tm6000_start_audio_dma(chip); + } else { + dprintk(1, "stopping capture"); + _tm6000_stop_audio_dma(chip); + } +} + +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + atomic_set(&core->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&core->stream_started, 0); + break; + default: + err = -EINVAL; + break; + } + schedule_work(&core->wq_trigger); + + return err; +} +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + return chip->buf_pos; +} + +/* + * operators + */ +static const struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, +}; + +/* + * create a PCM device + */ + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ +static int tm6000_audio_init(struct tm6000_core *dev) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc; + static int devnr; + char component[14]; + struct snd_pcm *pcm; + + if (!dev) + return 0; + + if (devnr >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[devnr]) + return -ENOENT; + + rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000", + THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); + return rc; + } + strscpy(card->driver, "tm6000-alsa", sizeof(card->driver)); + strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname)); + sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", + dev->udev->bus->busnum, dev->udev->devnum); + + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + + chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + dev->adev = chip; + spin_lock_init(&chip->reg_lock); + + rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); + if (rc < 0) + goto error_chip; + + pcm->info_flags = 0; + pcm->private_data = chip; + strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + rc = snd_card_register(card); + if (rc < 0) + goto error_chip; + + dprintk(1, "Registered audio driver for %s\n", card->longname); + + return 0; + +error_chip: + kfree(chip); + dev->adev = NULL; +error: + snd_card_free(card); + return rc; +} + +static int tm6000_audio_fini(struct tm6000_core *dev) +{ + struct snd_tm6000_card *chip; + + if (!dev) + return 0; + chip = dev->adev; + + if (!chip) + return 0; + + if (!chip->card) + return 0; + + snd_card_free(chip->card); + chip->card = NULL; + kfree(chip); + dev->adev = NULL; + + return 0; +} + +static struct tm6000_ops audio_ops = { + .type = TM6000_AUDIO, + .name = "TM6000 Audio Extension", + .init = tm6000_audio_init, + .fini = tm6000_audio_fini, + .fillbuf = tm6000_fillbuf, +}; + +static int __init tm6000_alsa_register(void) +{ + return tm6000_register_extension(&audio_ops); +} + +static void __exit tm6000_alsa_unregister(void) +{ + tm6000_unregister_extension(&audio_ops); +} + +module_init(tm6000_alsa_register); +module_exit(tm6000_alsa_unregister); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-cards.c b/drivers/staging/media/deprecated/tm6000/tm6000-cards.c new file mode 100644 index 000000000000..98f4a63adc2a --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-cards.c @@ -0,0 +1,1397 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "xc2028.h" +#include "xc5000.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 +#define TM6010_BOARD_BEHOLD_WANDER 10 +#define TM6010_BOARD_BEHOLD_VOYAGER 11 +#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 +#define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 + +#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ + (model == TM5600_BOARD_GENERIC) || \ + (model == TM6000_BOARD_GENERIC) || \ + (model == TM6010_BOARD_GENERIC)) + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + + +struct tm6000_board { + char *name; + char eename[16]; /* EEPROM name */ + unsigned eename_size; /* size of EEPROM name */ + unsigned eename_pos; /* Position where it appears at ROM */ + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + + struct tm6000_gpio gpio; + + struct tm6000_input vinput[3]; + struct tm6000_input rinput; + + char *ir_codes; +}; + +static struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, + .eename_size = 14, + .eename_pos = 0x14, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", + .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, + .eename_size = 14, + .eename_pos = 0x42, + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .ir_codes = RC_MAP_HAUPPAUGE, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER] = { + .name = "Beholder Wander DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER] = { + .name = "Beholder Voyager TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { + .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_SIF1, + }, + }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_TWINHAN_TU501] = { + .name = "Twinhan TU501(704D1)", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, +}; + +/* table of devices that work with this driver */ +static const struct usb_device_id tm6000_id_table[] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, + { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, + { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, + { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, + { } +}; +MODULE_DEVICE_TABLE(usb, tm6000_id_table); + +/* Control power led for show some activity */ +void tm6000_flash_led(struct tm6000_core *dev, u8 state) +{ + /* Power LED unconfigured */ + if (!dev->gpio.power_led) + return; + + /* ON Power LED */ + if (state) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + } + } + /* OFF Power LED */ + else { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + } + } +} + +/* Tuner callback to provide the proper gpio changes needed for xc5000 */ +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC5000) + return 0; + + switch (command) { + case XC5000_TUNER_RESET: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_ir_wait(dev, 0); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc = tm6000_i2c_reset(dev, 10); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + /* newer tuner can faster reset */ + switch (dev->model) { + case TM5600_BOARD_10MOONS_UT821: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x00); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + break; + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(75); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + break; + default: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(130); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(130); + break; + } + + tm6000_ir_wait(dev, 1); + break; + case 1: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + case 2: + rc = tm6000_i2c_reset(dev, 100); + break; + } + break; + case XC2028_I2C_FLUSH: + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_tuner_callback); + +int tm6000_cards_setup(struct tm6000_core *dev) +{ + /* + * Board-specific initialization sequence. Handles all GPIO + * initialization sequences that are board-specific. + * Up to now, all found devices use GPIO1 and GPIO4 at the same way. + * Probably, they're all based on some reference device. Due to that, + * there's a common routine at the end to handle those GPIO's. Devices + * that use different pinups or init sequences can just return at + * the board-specific session. + */ + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + case TM6010_BOARD_GENERIC: + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + /* Turn zarlink zl10353 off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); + msleep(15); + /* ir ? */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); + msleep(15); + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); + msleep(15); + /* DVB led off (orange) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + break; + default: + break; + } + + /* + * Default initialization. Most of the devices seem to use GPIO1 + * and GPIO4.on the same way, so, this handles the common sequence + * used by most devices. + * If a device uses a different sequence or different GPIO pins for + * reset, just add the code at the board-specific part + */ + + if (dev->gpio.tuner_reset) { + int rc; + int i; + + for (i = 0; i < 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + } + } else { + printk(KERN_ERR "Tuner reset is not configured\n"); + return -1; + } + + msleep(50); + + return 0; +}; + +static void tm6000_config_tuner(struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + tun_setup.mode_mask = 0; + if (dev->caps.has_tuner) + tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); + + switch (dev->tuner_type) { + case TUNER_XC2028: + tun_setup.tuner_callback = tm6000_tuner_callback; + break; + case TUNER_XC5000: + tun_setup.tuner_callback = tm6000_xc5000_callback; + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.demod = XC3028_FE_ZARLINK456; + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + ctl.max_len = 80; + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "xc3028-v24.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + + } + break; + case TUNER_XC5000: + { + struct v4l2_priv_tun_config xc5000_cfg; + struct xc5000_config ctl = { + .i2c_address = dev->tuner_addr, + .if_khz = 4570, + .radio_input = XC5000_RADIO_FM1_MONO, + }; + + xc5000_cfg.tuner = TUNER_XC5000; + xc5000_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc5000_cfg); + } + break; + default: + printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); + break; + } +} + +static int fill_board_specific_data(struct tm6000_core *dev) +{ + int rc; + + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + + dev->gpio = tm6000_boards[dev->model].gpio; + + dev->ir_codes = tm6000_boards[dev->model].ir_codes; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; + dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; + dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; + dev->rinput = tm6000_boards[dev->model].rinput; + + /* setup per-model quirks */ + switch (dev->model) { + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_HAUPPAUGE_900H: + dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; + break; + + default: + break; + } + + /* initialize hardware */ + rc = tm6000_init(dev); + if (rc < 0) + return rc; + + return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); +} + + +static void use_alternative_detection_method(struct tm6000_core *dev) +{ + int i, model = -1; + + if (!dev->eedata_size) + return; + + for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { + if (!tm6000_boards[i].eename_size) + continue; + if (dev->eedata_size < tm6000_boards[i].eename_pos + + tm6000_boards[i].eename_size) + continue; + + if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], + tm6000_boards[i].eename, + tm6000_boards[i].eename_size)) { + model = i; + break; + } + } + if (model < 0) { + printk(KERN_INFO "Device has eeprom but is currently unknown\n"); + return; + } + + dev->model = model; + + printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", + tm6000_boards[model].name, model); +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct tm6000_core *dev = container_of(work, struct tm6000_core, + request_module_wk); + + request_module("tm6000-alsa"); + + if (dev->caps.has_dvb) + request_module("tm6000-dvb"); +} + +static void request_modules(struct tm6000_core *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_modules(struct tm6000_core *dev) +{ + flush_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#define flush_request_modules(dev) +#endif /* CONFIG_MODULES */ + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + + if (!is_generic(dev->model)) { + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + } else { + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + + use_alternative_detection_method(dev); + + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + } + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_NTSC_M; + + /* Configure tuner */ + tm6000_config_tuner(dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", I2C_ADDR_TDA9874, NULL); + + /* register and initialize V4L2 */ + rc = tm6000_v4l2_register(dev); + if (rc < 0) + goto err; + + tm6000_add_into_devlist(dev); + tm6000_init_extension(dev); + + tm6000_ir_init(dev); + + request_modules(dev); + + mutex_unlock(&dev->lock); + return 0; + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint(struct usb_device *udev, + struct usb_host_interface *alt, + char *msgtype, + struct usb_host_endpoint *curr_e, + struct tm6000_endpoint *tm_ep) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(tmp); + + if (size > tm_ep->maxsize) { + tm_ep->endp = curr_e; + tm_ep->maxsize = size; + tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; + tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; + + printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev; + int i, rc; + int nr = 0; + char *speed; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc = usb_set_interface(usbdev, 0, 1); + if (rc < 0) + goto report_failure; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); + rc = -ENOMEM; + goto put_device; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + rc = -ENOMEM; + goto put_device; + } + spin_lock_init(&dev->slock); + mutex_init(&dev->usb_lock); + + /* Increment usage count */ + set_bit(nr, &tm6000_devused); + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model = id->driver_info; + if (card[nr] < ARRAY_SIZE(tm6000_boards)) + dev->model = card[nr]; + + dev->udev = usbdev; + dev->devno = nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk IN", e, + &dev->bulk_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk OUT", e, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC IN", e, + &dev->isoc_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC OUT", e, + &dev->isoc_out); + } + break; + case USB_ENDPOINT_XFER_INT: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT IN", e, + &dev->int_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT OUT", e, + &dev->int_out); + } + break; + } + } + } + + + printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in.endp) { + printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); + rc = -ENODEV; + goto free_device; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); + + rc = tm6000_init_dev(dev); + if (rc < 0) + goto free_device; + + return 0; + +free_device: + kfree(dev); +report_failure: + printk(KERN_ERR "tm6000: Error %d while registering\n", rc); + + clear_bit(nr, &tm6000_devused); +put_device: + usb_put_dev(usbdev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets disconnected + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); + + flush_request_modules(dev); + + tm6000_ir_fini(dev); + + if (dev->gpio.power_led) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + msleep(15); + break; + } + } + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + tm6000_close_extension(dev); + tm6000_remove_from_devlist(dev); + + clear_bit(dev->devno, &tm6000_devused); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +module_usb_driver(tm6000_usb_driver); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-core.c b/drivers/staging/media/deprecated/tm6000/tm6000-core.c new file mode 100644 index 000000000000..5c8cbc5d6f72 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-core.c @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab +// +// Copyright (c) 2007 Michel Ludwig +// - DVB-T support + +#include +#include +#include +#include +#include +#include "tm6000.h" +#include "tm6000-regs.h" +#include +#include + +#define USB_TIMEOUT (5 * HZ) /* ms */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + u8 *data = NULL; + int delay = 5000; + + if (len) { + data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + } + + mutex_lock(&dev->usb_lock); + + if (req_type & USB_DIR_IN) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else { + pipe = usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); + + printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN) ? " IN" : "OUT", + req_type, req, value&0xff, value>>8, index&0xff, + index>>8, len&0xff, len>>8); + + if (!(req_type & USB_DIR_IN)) { + printk(KERN_CONT ">>> "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, + data, len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret < 0) { + if (req_type & USB_DIR_IN) + printk(KERN_DEBUG "<<< (len=%d)\n", len); + + printk(KERN_CONT "%s: Error #%d\n", __func__, ret); + } else if (req_type & USB_DIR_IN) { + printk(KERN_CONT "<<< "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + kfree(data); + + if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) + delay = 0; + + if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { + unsigned int tsleep; + /* Calculate delay time, 14000us for 64 bytes */ + tsleep = (len * 200) + 200; + if (tsleep < delay) + tsleep = delay; + usleep_range(tsleep, tsleep + 1000); + } + else if (delay) + usleep_range(delay, delay + 1000); + + mutex_unlock(&dev->usb_lock); + return ret; +} + +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg); + +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + return *buf; +} +EXPORT_SYMBOL_GPL(tm6000_get_reg); + +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, 0, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == buf[0]) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc < 0) + return rc; + + return buf[1]|buf[0]<<8; +} + +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[4]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); + + if (rc < 0) + return rc; + + return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; +} + +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) +{ + int rc; + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + + msleep(tsleep); + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); + msleep(tsleep); + + return rc; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); + else + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + else + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); + } +} + +static void tm6000_set_vbi(struct tm6000_core *dev) +{ + /* + * FIXME: + * VBI lines and start/end are different between 60Hz and 50Hz + * So, it is very likely that we need to change the config to + * something that takes it into account, doing something different + * if (dev->norm & V4L2_STD_525_60) + */ + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); + tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); + tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); + tm6000_set_reg(dev, + TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); + tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); + tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); + tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); + tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); + tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } +} + +int tm6000_init_analog_mode(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + + if (dev->dev_type == TM6010) { + u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; + + if (!dev->radio) + active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; + + /* Enable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + active, 0x60); + /* Disable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + + if (dev->scaler) + /* Disable Hfilter and Enable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); + else /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); + + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + + /* + * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected + * for more than a few seconds. Not sure why, as this behavior does + * not happen on other devices with xc3028. So, I suspect that it + * is yet another bug at tm6000. After start sleeping, decoding + * doesn't start automatically. Instead, it requires some + * I2C commands to wake it up. As we want to have image at the + * beginning, we needed to add this hack. The better would be to + * discover some way to make tm6000 to wake up without this hack. + */ + f.frequency = dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + msleep(100); + tm6000_set_standard(dev); + tm6000_set_vbi(dev); + tm6000_set_audio_bitrate(dev, 48000); + + /* switch dvb led off */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x01); + } + + return 0; +} + +int tm6000_init_digital_mode(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Disable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + 0x00, 0x60); + /* Enable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x40, 0x40); + /* all power down, but not the digital data port */ + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); + } else { + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); + + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); + msleep(50); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + } + + /* switch dvb led on */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x00); + } + + return 0; +} +EXPORT_SYMBOL(tm6000_init_digital_mode); + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +static struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, + { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ + { TM6010_REQ05_R18_IMASK7, 0x00 }, +}; + +static struct reg_init tm6010_init_tab[] = { + { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, + { TM6010_REQ07_RC4_HSTART0, 0xa0 }, + { TM6010_REQ07_RC6_HEND0, 0x40 }, + { TM6010_REQ07_RCA_VEND0, 0x31 }, + { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, + { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, + { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, + + { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, + { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, + { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, + { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, + { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, + { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, + { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, + { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + + { TM6010_REQ05_R18_IMASK7, 0x00 }, + + { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, + { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, + { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, + { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, + { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, + { TM6010_REQ07_RD8_IR, 0x0f }, + + /* set remote wakeup key:any key wakeup */ + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, + { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, +}; + +int tm6000_init(struct tm6000_core *dev) +{ + int board, rc = 0, i, size; + struct reg_init *tab; + + /* Check board revision */ + board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (board >= 0) { + switch (board & 0xff) { + case 0xf3: + printk(KERN_INFO "Found tm6000\n"); + if (dev->dev_type != TM6000) + dev->dev_type = TM6000; + break; + case 0xf4: + printk(KERN_INFO "Found tm6010\n"); + if (dev->dev_type != TM6010) + dev->dev_type = TM6010; + break; + default: + printk(KERN_INFO "Unknown board version = 0x%08x\n", board); + } + } else + printk(KERN_ERR "Error %i while retrieving board version\n", board); + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i = 0; i < size; i++) { + rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", + rc, + tab[i].req, tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + rc = tm6000_cards_setup(dev); + + return rc; +} + + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + + + /* enable I2S, if we use sif or external I2S device */ + if (dev->dev_type == TM6010) { + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); + if (val < 0) + return val; + + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); + if (val < 0) + return val; + } else { + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); + if (val < 0) + return val; + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +int tm6000_set_audio_rinput(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0; + u8 areg_07 = 0x10; + + switch (dev->rinput.amux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + areg_f0 = 0x03; + areg_07 = 0x30; + break; + case TM6000_AMUX_ADC1: + areg_f0 = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_f0 = 0x08; + break; + case TM6000_AMUX_I2S: + areg_f0 = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input doesn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + areg_07, 0xf0); + } else { + u8 areg_eb; + /* Audio setting, default LINE1 */ + switch (dev->rinput.amux) { + case TM6000_AMUX_ADC1: + areg_eb = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_eb = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input doesn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} + +static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_mux mux; + + if (dev->radio) + mux = dev->rinput.amux; + else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", + dev->name); + return -EINVAL; + } + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); +} + +static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_mux mux; + + if (dev->radio) { + mux = dev->rinput.amux; + vol += 8; /* Offset to 0 dB */ + } else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", + dev->name); + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} + +static LIST_HEAD(tm6000_devlist); +static DEFINE_MUTEX(tm6000_devlist_mutex); + +/* + * tm6000_realease_resource() + */ + +void tm6000_remove_from_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +void tm6000_add_into_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&dev->devlist, &tm6000_devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(tm6000_extension_devlist); + +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size) +{ + struct tm6000_ops *ops = NULL; + + /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ + + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); + } + + return 0; +} + +int tm6000_register_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&ops->next, &tm6000_extension_devlist); + list_for_each_entry(dev, &tm6000_devlist, devlist) { + ops->init(dev); + printk(KERN_INFO "%s: Initialized (%s) extension\n", + dev->name, ops->name); + } + mutex_unlock(&tm6000_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(tm6000_register_extension); + +void tm6000_unregister_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(dev, &tm6000_devlist, devlist) + ops->fini(dev); + + printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&tm6000_devlist_mutex); +} +EXPORT_SYMBOL(tm6000_unregister_extension); + +void tm6000_init_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + mutex_unlock(&tm6000_devlist_mutex); +} + +void tm6000_close_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + mutex_unlock(&tm6000_devlist_mutex); +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c b/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c new file mode 100644 index 000000000000..ee04973cbf93 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Michel Ludwig + */ + +#include +#include +#include + +#include "tm6000.h" +#include "tm6000-regs.h" + +#include "zl10353.h" + +#include + +#include "xc2028.h" +#include "xc5000.h" + +MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug message"); + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, 1, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, 1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static void tm6000_urb_received(struct urb *urb) +{ + int ret; + struct tm6000_core *dev = urb->context; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + default: + print_err_status(dev, 0, urb->status); + } + + if (urb->actual_length > 0) + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + + if (dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __func__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + dev->dvb->bulk_urb = NULL; + } + } +} + +static int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, size; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); + + if (dev->mode != TM6000_MODE_DIGITAL) { + tm6000_init_digital_mode(dev); + dev->mode = TM6000_MODE_DIGITAL; + } + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dvb->bulk_urb) + return -ENOMEM; + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + size = size * 15; /* 512 x 8 or 12 or 15 */ + + dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (!dvb->bulk_urb->transfer_buffer) { + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return -ENOMEM; + } + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + size, + tm6000_urb_received, dev); + + ret = usb_clear_halt(dev->udev, pipe); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", + ret, __func__); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return ret; + } else + printk(KERN_ERR "tm6000: pipe reset\n"); + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC); + +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", + ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return ret; + } + + return 0; +} + +static void tm6000_stop_stream(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + printk(KERN_INFO "urb killing\n"); + usb_kill_urb(dvb->bulk_urb); + printk(KERN_INFO "urb buffer free\n"); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +static int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + if (dvb->streams == 0) { + dvb->streams = 1; +/* mutex_init(&tm6000_dev->streming_mutex); */ + tm6000_start_stream(dev); + } else + ++(dvb->streams); + mutex_unlock(&dvb->mutex); + + return 0; +} + +static int tm6000_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + + printk(KERN_INFO "stream %#x\n", dvb->streams); + --(dvb->streams); + if (dvb->streams == 0) { + printk(KERN_INFO "stop stream\n"); + tm6000_stop_stream(dev); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + } + mutex_unlock(&dvb->mutex); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + + return 0; +} + +static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dev->caps.has_zl10353) { + struct zl10353_config config = { + .demod_address = dev->demod_addr, + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45700, + .disable_i2c_gate_ctrl = 1, + }; + + dvb->frontend = dvb_attach(zl10353_attach, &config, + &dev->i2c_adap); + } else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int register_dvb(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if (ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + if (ret < 0) { + pr_err("tm6000: couldn't register the adapter!\n"); + goto err; + } + + dvb->adapter.priv = dev; + + if (dvb->frontend) { + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_tuner_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be attached to frontend!\n"); + break; + } + case TUNER_XC5000: { + struct xc5000_config cfg = { + .i2c_address = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_xc5000_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register frontend (xc5000)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC5000 asked to be attached to frontend!\n"); + break; + } + } + } else + printk(KERN_ERR "tm6000: no frontend found\n"); + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 8; + dvb->demux.feednum = 8; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if (dvb->frontend) { + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +static void unregister_dvb(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + if (dvb->frontend) { + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ +} + +static int dvb_init(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb; + int rc; + + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->udev->speed == USB_SPEED_FULL) { + printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n"); + return 0; + } + + dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); + if (!dvb) + return -ENOMEM; + + dev->dvb = dvb; + + rc = register_dvb(dev); + if (rc < 0) { + kfree(dvb); + dev->dvb = NULL; + return 0; + } + + return 0; +} + +static int dvb_fini(struct tm6000_core *dev) +{ + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->dvb) { + unregister_dvb(dev); + kfree(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct tm6000_ops dvb_ops = { + .type = TM6000_DVB, + .name = "TM6000 dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init tm6000_dvb_register(void) +{ + return tm6000_register_extension(&dvb_ops); +} + +static void __exit tm6000_dvb_unregister(void) +{ + tm6000_unregister_extension(&dvb_ops); +} + +module_init(tm6000_dvb_register); +module_exit(tm6000_dvb_unregister); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c b/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c new file mode 100644 index 000000000000..7554b93b82e6 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab +// +// Copyright (c) 2007 Michel Ludwig +// - Fix SMBus Read Byte command + +#include +#include +#include +#include + +#include "tm6000.h" +#include "tm6000-regs.h" +#include +#include +#include "xc2028.h" + + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__, ##args); } while (0) + +static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 80; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | reg << 8, 0, buf, len); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + /* release mutex */ + return rc; +} + +/* Generic read - doesn't work fine with 16bit registers */ +static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + u8 b[2]; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { + /* + * Workaround an I2C bug when reading from zl10353 + */ + reg -= 1; + len += 1; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); + + *buf = b[1]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); + } + + /* release mutex */ + return rc; +} + +/* + * read from a 16bit register + * for example xc2028, xc3028 or xc3028L + */ +static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, + __u16 reg, char *buf, int len) +{ + int rc; + unsigned char ureg; + + if (!buf || len != 2) + return -1; + + /* capture mutex */ + if (dev->dev_type == TM6010) { + ureg = reg & 0xFF; + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | (reg & 0xFF00), 0, &ureg, 1); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, + reg, 0, buf, len); + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, + addr, reg, buf, len); + } + + /* release mutex */ + return rc; +} + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2, "%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfill this request. + */ + i2c_dprintk(2, " read without preceding write not supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + + if (msgs[i].len == 2) { + rc = tm6000_i2c_recv_regs16(dev, addr, + msgs[i].buf[0] << 8 | msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + } else { + rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf, msgs[i + 1].len); + } + + i++; + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], + msgs[i].buf + 1, msgs[i].len - 1); + } + if (i2c_debug >= 2) + printk(KERN_CONT "\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2, " ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev) +{ + int i, rc; + unsigned char *p = dev->eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + dev->eedata_size = 0; + + bytes[16] = '\0'; + for (i = 0; i < sizeof(dev->eedata); ) { + *p = i; + rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); + if (rc < 1) { + if (p == dev->eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -EINVAL; + } + dev->eedata_size++; + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(KERN_CONT " %02x", dev->eedata[i]); + if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) + bytes[i%16] = dev->eedata[i]; + else + bytes[i%16] = '.'; + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(KERN_CONT " %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(KERN_CONT " "); + printk(KERN_CONT " %s\n", bytes); + } + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return -EINVAL; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + int rc; + + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.algo = &tm6000_algo; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strscpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + rc = i2c_add_adapter(&dev->i2c_adap); + if (rc) + return rc; + + dev->i2c_client.adapter = &dev->i2c_adap; + strscpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); + tm6000_i2c_eeprom(dev); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-input.c b/drivers/staging/media/deprecated/tm6000/tm6000-input.c new file mode 100644 index 000000000000..5136e9e202f1 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-input.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2010 Stefan Ringel + */ + +#include +#include +#include + +#include +#include + +#include + +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "debug message level"); + +static unsigned int enable_ir = 1; +module_param(enable_ir, int, 0644); +MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); + +static unsigned int ir_clock_mhz = 12; +module_param(ir_clock_mhz, int, 0644); +MODULE_PARM_DESC(ir_clock_mhz, "ir clock, in MHz"); + +#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ +#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ + +#undef dprintk + +#define dprintk(level, fmt, arg...) do {\ + if (ir_debug >= level) \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } while (0) + +struct tm6000_ir_poll_result { + u16 rc_data; +}; + +struct tm6000_IR { + struct tm6000_core *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll expernal decoder */ + int polling; + struct delayed_work work; + u8 wait:1; + u8 pwled:2; + u8 submit_urb:1; + struct urb *int_urb; + + /* IR device properties */ + u64 rc_proto; +}; + +void tm6000_ir_wait(struct tm6000_core *dev, u8 state) +{ + struct tm6000_IR *ir = dev->ir; + + if (!dev->ir) + return; + + dprintk(2, "%s: %i\n",__func__, ir->wait); + + if (state) + ir->wait = 1; + else + ir->wait = 0; +} + +static int tm6000_ir_config(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + u32 pulse = 0, leader = 0; + + dprintk(2, "%s\n",__func__); + + /* + * The IR decoder supports RC-5 or NEC, with a configurable timing. + * The timing configuration there is not that accurate, as it uses + * approximate values. The NEC spec mentions a 562.5 unit period, + * and RC-5 uses a 888.8 period. + * Currently, driver assumes a clock provided by a 12 MHz XTAL, but + * a modprobe parameter can adjust it. + * Adjustments are required for other timings. + * It seems that the 900ms timing for NEC is used to detect a RC-5 + * IR, in order to discard such decoding + */ + + switch (ir->rc_proto) { + case RC_PROTO_BIT_NEC: + leader = 900; /* ms */ + pulse = 700; /* ms - the actual value would be 562 */ + break; + default: + case RC_PROTO_BIT_RC5: + leader = 900; /* ms - from the NEC decoding */ + pulse = 1780; /* ms - The actual value would be 1776 */ + break; + } + + pulse = ir_clock_mhz * pulse; + leader = ir_clock_mhz * leader; + if (ir->rc_proto == RC_PROTO_BIT_NEC) + leader = leader | 0x8000; + + dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", + __func__, + (ir->rc_proto == RC_PROTO_BIT_NEC) ? "NEC" : "RC-5", + ir_clock_mhz, leader, pulse); + + /* Remote WAKEUP = enable, normal mode, from IR decoder output */ + tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); + + /* Enable IR reception on non-busrt mode */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); + + /* IR_WKUP_SEL = Low byte in decoded IR data */ + tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); + /* IR_WKU_ADD code */ + tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); + + tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); + + tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); + + if (!ir->polling) + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + else + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); + msleep(10); + + /* Shows that IR is working via the LED */ + tm6000_flash_led(dev, 0); + msleep(100); + tm6000_flash_led(dev, 1); + ir->pwled = 1; + + return 0; +} + +static void tm6000_ir_keydown(struct tm6000_IR *ir, + const char *buf, unsigned int len) +{ + u8 device, command; + u32 scancode; + enum rc_proto protocol; + + if (len < 1) + return; + + command = buf[0]; + device = (len > 1 ? buf[1] : 0x0); + switch (ir->rc_proto) { + case RC_PROTO_BIT_RC5: + protocol = RC_PROTO_RC5; + scancode = RC_SCANCODE_RC5(device, command); + break; + case RC_PROTO_BIT_NEC: + protocol = RC_PROTO_NEC; + scancode = RC_SCANCODE_NEC(device, command); + break; + default: + protocol = RC_PROTO_OTHER; + scancode = RC_SCANCODE_OTHER(device << 8 | command); + break; + } + + dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); +} + +static void tm6000_ir_urb_received(struct urb *urb) +{ + struct tm6000_core *dev = urb->context; + struct tm6000_IR *ir = dev->ir; + char *buf; + + dprintk(2, "%s\n",__func__); + if (urb->status < 0 || urb->actual_length <= 0) { + printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", + urb->status, urb->actual_length); + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + buf = urb->transfer_buffer; + + if (ir_debug) + print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", + DUMP_PREFIX_OFFSET,16, 1, + buf, urb->actual_length, false); + + tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); + + usb_submit_urb(urb, GFP_ATOMIC); + /* + * Flash the led. We can't do it here, as it is running on IRQ context. + * So, use the scheduler to do it, in a few ms. + */ + ir->pwled = 2; + schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); +} + +static void tm6000_ir_handle_key(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + int rc; + u8 buf[2]; + + if (ir->wait) + return; + + dprintk(3, "%s\n",__func__); + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_02_GET_IR_CODE, 0, 0, buf, 2); + if (rc < 0) + return; + + /* Check if something was read */ + if ((buf[0] & 0xff) == 0xff) { + if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } + return; + } + + tm6000_ir_keydown(ir, buf, rc); + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + /* Re-schedule polling */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static void tm6000_ir_int_work(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + int rc; + + dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, + ir->pwled); + + if (ir->submit_urb) { + dprintk(3, "Resubmit urb\n"); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + + rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); + if (rc < 0) { + printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", + rc); + /* Retry in 100 ms */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + ir->submit_urb = 0; + } + + /* Led is enabled only if USB submit doesn't fail */ + if (ir->pwled == 2) { + tm6000_flash_led(dev, 0); + ir->pwled = 0; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); + } else if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } +} + +static int tm6000_ir_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void tm6000_ir_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + cancel_delayed_work_sync(&ir->work); +} + +static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + ir->rc_proto = *rc_proto; + + tm6000_ir_config(ir); + /* TODO */ + return 0; +} + +static int __tm6000_ir_int_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + struct tm6000_core *dev; + int pipe, size; + int err = -ENOMEM; + + if (!ir) + return -ENODEV; + dev = ir->dev; + + dprintk(2, "%s\n",__func__); + + ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!ir->int_urb) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + dprintk(1, "IR max size: %d\n", size); + + ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); + if (!ir->int_urb->transfer_buffer) { + usb_free_urb(ir->int_urb); + return err; + } + dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); + + usb_fill_int_urb(ir->int_urb, dev->udev, pipe, + ir->int_urb->transfer_buffer, size, + tm6000_ir_urb_received, dev, + dev->int_in.endp->desc.bInterval); + + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + + return 0; +} + +static void __tm6000_ir_int_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir || !ir->int_urb) + return; + + dprintk(2, "%s\n",__func__); + + usb_kill_urb(ir->int_urb); + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + ir->int_urb = NULL; +} + +int tm6000_ir_int_start(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir) + return 0; + + return __tm6000_ir_int_start(ir->rc); +} + +void tm6000_ir_int_stop(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir || !ir->rc) + return; + + __tm6000_ir_int_stop(ir->rc); +} + +int tm6000_ir_init(struct tm6000_core *dev) +{ + struct tm6000_IR *ir; + struct rc_dev *rc; + int err = -ENOMEM; + u64 rc_proto; + + if (!enable_ir) + return -ENODEV; + + if (!dev->caps.has_remote) + return 0; + + if (!dev->ir_codes) + return 0; + + ir = kzalloc(sizeof(*ir), GFP_ATOMIC); + rc = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!ir || !rc) + goto out; + + dprintk(2, "%s\n", __func__); + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + /* input setup */ + rc->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_NEC; + /* Needed, in order to support NEC remotes with 24 or 32 bits */ + rc->scancode_mask = 0xffff; + rc->priv = ir; + rc->change_protocol = tm6000_ir_change_protocol; + if (dev->int_in.endp) { + rc->open = __tm6000_ir_int_start; + rc->close = __tm6000_ir_int_stop; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); + } else { + rc->open = tm6000_ir_start; + rc->close = tm6000_ir_stop; + ir->polling = 50; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); + } + + snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + rc_proto = RC_PROTO_BIT_UNKNOWN; + tm6000_ir_change_protocol(rc, &rc_proto); + + rc->device_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->map_name = dev->ir_codes; + rc->driver_name = "tm6000"; + rc->dev.parent = &dev->udev->dev; + + /* ir register */ + err = rc_register_device(rc); + if (err) + goto out; + + return 0; + +out: + dev->ir = NULL; + rc_free_device(rc); + kfree(ir); + return err; +} + +int tm6000_ir_fini(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + /* skip detach on non attached board */ + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + if (!ir->polling) + __tm6000_ir_int_stop(ir->rc); + + tm6000_ir_stop(ir->rc); + + /* Turn off the led */ + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + rc_unregister_device(ir->rc); + + kfree(ir); + dev->ir = NULL; + + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-regs.h b/drivers/staging/media/deprecated/tm6000/tm6000-regs.h new file mode 100644 index 000000000000..6a181f2e7ef2 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-regs.h @@ -0,0 +1,588 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab + */ + +/* + * Define TV Master TM5600/TM6000/TM6010 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ +#define REQ_20_DATA_TRANSFER 20 +#define REQ_30_I2C_WRITE 30 +#define REQ_31_I2C_READ 31 +#define REQ_35_AFTEK_TUNER_READ 35 +#define REQ_40_GET_VERSION 40 +#define REQ_50_SET_START 50 +#define REQ_51_SET_STOP 51 +#define REQ_52_TRANSMIT_DATA 52 +#define REQ_53_SPI_INITIAL 53 +#define REQ_54_SPI_SETSTART 54 +#define REQ_55_SPI_INOUTDATA 55 +#define REQ_56_SPI_SETSTOP 56 + +/* + * Define TV Master TM5600/TM6000/TM6010 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000/TM6010 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO = 1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + +/* Define TM6000/TM6010 Video decoder registers */ +#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 +#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 +#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 +#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 +#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 +#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 +#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 +#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 +#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 +#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 +#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a +#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b +#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c +#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d +#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f +#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 +#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 +#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 +#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 +#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 +#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 +#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 +#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 +#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 +#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 +#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a +#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b +#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c +#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d +#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e +#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f +#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 +#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 +#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 +#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 +#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 +#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 +#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 +#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 +#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 +#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 +#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a +#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b +#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c +#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d +#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e +#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f +#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 +#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 +#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 +#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 +#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 +#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 +#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 +#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 +#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 +#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 +#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a +#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b +#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c +#define TM6010_REQ07_R3F_RESET 0x07, 0x3f +#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 +#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 +#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 +#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 +#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 +#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 +#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 +#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 +#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 +#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 +#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a +#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b +#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c +#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d +#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e +#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f +#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 +#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 +#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 +#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 +#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 +#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 +#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 +#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 +#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 +#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 +#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a +#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b +#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c +#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d +#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e +#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f +#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 +#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 +#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 +#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 +#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 +#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 +#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 +#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 +#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 +#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 +#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 +#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 +#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 +#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 +#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 +#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 +#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 +#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 +#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 +#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a +#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b +#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c +#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d +#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f +#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 +#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 +#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 +#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 +#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 +#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 +#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 +#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a +#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b +#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d +#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e +#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f + +/* Define TM6000/TM6010 Miscellaneous registers */ +#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 +#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 +#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 +#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 +#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 +#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 +#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 +#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 +#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 +#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 +#define TM6010_REQ07_RCA_VEND0 0x07, 0xca +#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc +#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) +#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) +#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 +#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 +#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 +#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 +#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 +#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 +#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 +#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe +#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff + +/* Define TM6000/TM6010 USB registers */ +#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 +#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 +#define TM6010_REQ05_R02_TEST 0x05, 0x02 +#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 +#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 +#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 +#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 +#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 +#define TM6010_REQ05_R09_VCTL 0x05, 0x09 +#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a +#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b +#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c +#define TM6010_REQ05_R10_GMASK 0x05, 0x10 +#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 +#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 +#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 +#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 +#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 +#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 +#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 +#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 +#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 +#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a +#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c +#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d +#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 +#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 +#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 +#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 +#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 +#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 +#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 +#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 +#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 +#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 +#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a +#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b +#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c +#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d +#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e +#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f +#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 +#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 +#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 +#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 +#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 +#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 +#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 +#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 +#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 +#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 +#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a +#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b +#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c +#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d +#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e +#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 +#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 +#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 +#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 +#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 +#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 +#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 +#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 +#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 +#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a +#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b +#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c +#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d +#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e +#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f +#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 +#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 +#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 +#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 +#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 +#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 +#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a +#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b +#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c +#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d +#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 +#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 +#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 +#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 +#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 +#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 +#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 +#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 +#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 +#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a +#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b +#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c +#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d +#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e +#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f +#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 +#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b +#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d +#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 +#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 +#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 +#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 +#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 +#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 +#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 +#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 +#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 +#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 +#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a +#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b +#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c +#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d +#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e +#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f +#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 +#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 +#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 +#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 +#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 +#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 +#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 +#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 +#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 +#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 +#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a +#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b +#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c +#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d +#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e +#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f +#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 +#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 +#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 +#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 +#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 +#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 +#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 +#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 +#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 +#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 +#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa +#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab +#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac +#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad +#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae +#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf +#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 +#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 +#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 +#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 +#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 +#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 +#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 +#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 +#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 +#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 +#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba +#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb +#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc +#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd +#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe +#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf +#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 +#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 +#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 +#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc +#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 +#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 +#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 +#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc +#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 +#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 +#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 +#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec +#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 +#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 +#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 +#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc + +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ +#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 +#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 +#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 +#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 +#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 +#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 +#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 +#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 +#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 +#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 +#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a +#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b +#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c +#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d +#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e +#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f +#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 +#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 +#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 +#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 +#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 +#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 +#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 +#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 +#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 +#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 +#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a +#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b +#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e +#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f +#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 +#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 +#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 +#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 +#define TM6010_REQ08_R24_A_SER 0x08, 0x24 +#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 +#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 +#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 +#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 + +/* Define TM6010 Video ADC registers */ +#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 +#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 +#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 +#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 +#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 +#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 +#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 +#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 +#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 +#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 +#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea +#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb +#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec +#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed + +/* Define TM6010 Audio ADC registers */ +#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 +#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 +#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 +#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-stds.c b/drivers/staging/media/deprecated/tm6000/tm6000-stds.c new file mode 100644 index 000000000000..858cb4f3a9ca --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-stds.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2007 Mauro Carvalho Chehab + +#include +#include +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int tm6010_a_mode; +module_param(tm6010_a_mode, int, 0644); +MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings *common; +}; + +static struct tm6000_reg_settings composite_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings composite_stds[] = { + { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, + { .id = V4L2_STD_PAL, .common = composite_pal, }, + { .id = V4L2_STD_SECAM, .common = composite_secam, }, + { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, +}; + +static struct tm6000_reg_settings svideo_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings svideo_stds[] = { + { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, + { .id = V4L2_STD_PAL, .common = svideo_pal, }, + { .id = V4L2_STD_SECAM, .common = svideo_secam, }, + { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, +}; + +static int tm6000_set_audio_std(struct tm6000_core *dev) +{ + uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ + uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ + uint8_t areg_06 = 0x02; /* Auto de-emphasis, manual channel mode */ + + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + /* set mono or stereo */ + if (dev->amode == V4L2_TUNER_MODE_MONO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + else if (dev->amode == V4L2_TUNER_MODE_STEREO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); + return 0; + } + + /* + * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one + * audio standard for each V4L2_STD type. + */ + if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) { + areg_05 |= 0x04; + } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) { + areg_05 |= 0x43; + } else if (dev->norm & V4L2_STD_MN) { + areg_05 |= 0x22; + } else switch (tm6010_a_mode) { + /* auto */ + case 0: + if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L) + areg_05 |= 0x00; + else /* Other PAL/SECAM standards */ + areg_05 |= 0x10; + break; + /* A2 */ + case 1: + if (dev->norm & V4L2_STD_DK) + areg_05 = 0x09; + else + areg_05 = 0x05; + break; + /* NICAM */ + case 2: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x06; + } else if (dev->norm & V4L2_STD_PAL_I) { + areg_05 = 0x08; + } else if (dev->norm & V4L2_STD_SECAM_L) { + areg_05 = 0x0a; + areg_02 = 0x02; + } else { + areg_05 = 0x07; + } + break; + /* other */ + case 3: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x0b; + } else { + areg_05 = 0x02; + } + break; + } + + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); + tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); + tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); + tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); + tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); + tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); + tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + + return 0; +} + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) + dev->height = 480; + else + dev->height = 576; + + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; set[i].req; i++) { + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +int tm6000_set_standard(struct tm6000_core *dev) +{ + struct tm6000_input *input; + int i, rc = 0; + u8 reg_07_fe = 0x8a; + u8 reg_08_f1 = 0xfc; + u8 reg_08_e2 = 0xf0; + u8 reg_08_e6 = 0x0f; + + tm6000_get_std_res(dev); + + if (!dev->radio) + input = &dev->vinput[dev->input]; + else + input = &dev->rinput; + + if (dev->dev_type == TM6010) { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); + reg_08_e6 = 0x00; + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x00, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x08, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_SIF1: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + case TM6000_AMUX_SIF2: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + default: + break; + } + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); + } else { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); + break; + default: + break; + } + } + if (input->type == TM6000_INPUT_SVIDEO) { + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (dev->norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common); + goto ret; + } + } + return -EINVAL; + } else { + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (dev->norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, composite_stds[i].common); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + if ((dev->dev_type == TM6010) && + ((input->amux == TM6000_AMUX_SIF1) || + (input->amux == TM6000_AMUX_SIF2))) + tm6000_set_audio_std(dev); + + msleep(40); + + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h b/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h new file mode 100644 index 000000000000..e3c6933f854d --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab + */ + +#include + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int vfield, field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; +}; diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-video.c b/drivers/staging/media/deprecated/tm6000/tm6000-video.c new file mode 100644 index 000000000000..d855a19551f3 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-video.c @@ -0,0 +1,1705 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab +// +// Copyright (c) 2007 Michel Ludwig +// - Fixed module load/unload + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_NUM_URB_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static bool keep_urb; /* keep urb buffers allocated */ + +/* Debug level */ +int tm6000_debug; +EXPORT_SYMBOL_GPL(tm6000_debug); + +static struct tm6000_fmt format[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +/* ------------------------------------------------------------------ + * DMA and thread functions + * ------------------------------------------------------------------ + */ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + buf->vb.ts = ktime_get_ns(); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_streams(u8 *data, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 *ptr = data, *endp = data+len; + unsigned long header = 0; + int rc = 0; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + struct tm6000_buffer *vbuf = NULL; + char *voutp = NULL; + unsigned int linewidth; + + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } + + for (ptr = data; ptr < endp;) { + if (!dev->isoc_ctl.cmd) { + /* Header */ + if (dev->isoc_ctl.tmp_buf_len > 0) { + /* from last urb or packet */ + header = dev->isoc_ctl.tmp_buf; + if (4 - dev->isoc_ctl.tmp_buf_len > 0) { + memcpy((u8 *)&header + + dev->isoc_ctl.tmp_buf_len, + ptr, + 4 - dev->isoc_ctl.tmp_buf_len); + ptr += 4 - dev->isoc_ctl.tmp_buf_len; + } + dev->isoc_ctl.tmp_buf_len = 0; + } else { + if (ptr + 3 >= endp) { + /* have incomplete header */ + dev->isoc_ctl.tmp_buf_len = endp - ptr; + memcpy(&dev->isoc_ctl.tmp_buf, ptr, + dev->isoc_ctl.tmp_buf_len); + return rc; + } + /* Seek for sync */ + for (; ptr < endp - 3; ptr++) { + if (*(ptr + 3) == 0x47) + break; + } + /* Get message header */ + header = *(unsigned long *)ptr; + ptr += 4; + } + + /* split the header fields */ + size = ((header & 0x7e) << 1); + if (size > 0) + size -= 4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + /* Validates header fields */ + if (size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + pktsize = TM6000_URB_MSG_LEN; + /* + * calculate position in buffer and change the buffer + */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { + /* + * Announces that a new buffer + * were filled + */ + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } + break; + case TM6000_URB_MSG_VBI: + break; + case TM6000_URB_MSG_AUDIO: + case TM6000_URB_MSG_PTS: + size = pktsize; /* Size is always 180 bytes */ + break; + } + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size = dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + field = dev->isoc_ctl.field; + } + cpysize = (endp - ptr > size) ? size : endp - ptr; + if (cpysize) { + /* copy data in different buffers */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + if (vbuf) + memcpy(&voutp[pos], ptr, cpysize); + break; + case TM6000_URB_MSG_AUDIO: { + int i; + for (i = 0; i < cpysize; i += 2) + swab16s((u16 *)(ptr + i)); + + tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); + break; + } + case TM6000_URB_MSG_VBI: + /* Need some code to copy vbi buffer */ + break; + case TM6000_URB_MSG_PTS: { + /* Need some code to copy pts */ + u32 pts; + pts = *(u32 *)ptr; + dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", + field, pts); + break; + } + } + } + if (ptr + pktsize > endp) { + /* + * End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos + cpysize; + dev->isoc_ctl.size = size - cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.field = field; + dev->isoc_ctl.pktsize = pktsize - (endp - ptr); + ptr += endp - ptr; + } else { + dev->isoc_ctl.cmd = 0; + ptr += pktsize; + } + } + return 0; +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned int pos = dev->isoc_ctl.pos, cpysize; + int rc = 1; + struct tm6000_buffer *buf; + char *outp = NULL; + + get_next_buf(dma_q, &buf); + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + while (len > 0) { + cpysize = min(len, buf->vb.size-pos); + memcpy(&outp[pos], ptr, cpysize); + pos += cpysize; + ptr += cpysize; + len -= cpysize; + if (pos >= buf->vb.size) { + pos = 0; + /* Announces that a new buffer were filled */ + buffer_filled(dev, dma_q, buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf(dma_q, &buf); + if (!buf) + break; + outp = videobuf_to_vmalloc(&(buf->vb)); + if (!outp) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos = pos; + return rc; +} + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i, len = 0, rc = 1, status; + char *p; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + continue; + } + + len = urb->iso_frame_desc[i].actual_length; + + if (len > 0) { + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { + rc = copy_multiplexed(p, len, urb); + if (rc <= 0) + return rc; + } else { + copy_streams(p, len, urb); + } + } + } + } + return rc; +} + +/* ------------------------------------------------------------------ + * URB control + * ------------------------------------------------------------------ + */ + +/* + * IRQ callback, called by URB callback + */ +static void tm6000_irq_callback(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned long flags; + int i; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + tm6000_err("urb completion error %d.\n", urb->status); + break; + } + + spin_lock_irqsave(&dev->slock, flags); + tm6000_isoc_copy(urb); + spin_unlock_irqrestore(&dev->slock, flags); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer) + return 0; + + dev->urb_buffer = kmalloc_array(num_bufs, sizeof(*dev->urb_buffer), + GFP_KERNEL); + if (!dev->urb_buffer) + return -ENOMEM; + + dev->urb_dma = kmalloc_array(num_bufs, sizeof(*dev->urb_dma), + GFP_KERNEL); + if (!dev->urb_dma) + return -ENOMEM; + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (!dev->urb_buffer) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + if (!keep_urb) + tm6000_free_urb_buffers(dev); + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Assign URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, + dev->isoc_in.bAlternateSetting); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in.endp->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + + if (size > dev->isoc_in.maxsize) + size = dev->isoc_in.maxsize; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = TM6000_MAX_ISO_PACKETS; + sb_size = max_packets * size; + dev->urb_size = sb_size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc_array(num_bufs, sizeof(void *), + GFP_KERNEL); + if (!dev->isoc_ctl.urb) + return -ENOMEM; + + dev->isoc_ctl.transfer_buffer = kmalloc_array(num_bufs, + sizeof(void *), + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->isoc_in.maxsize, size); + + + if (tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_uninit_isoc(dev); + tm6000_free_urb_buffers(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in.endp->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + * Videobuf operations + * ------------------------------------------------------------------ + */ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) + *count = TM6000_MIN_BUF; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_core *dev = fh->dev; + int rc = 0; + + BUG_ON(NULL == fh->fmt); + + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + } + + if (!dev->isoc_ctl.num_bufs) { + rc = tm6000_prepare_isoc(dev); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + * IOCTL handling + * ------------------------------------------------------------------ + */ + +static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read) + return true; + + return false; +} + +static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh) + return true; + + return false; +} + +static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, + bool is_res_read) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read == is_res_read) + return true; + + /* is it free? */ + if (dev->resources) + return false; + + /* grab it */ + dev->resources = fh; + dev->is_res_read = is_res_read; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + return true; +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources != fh) + return; + + dev->resources = NULL; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); +} + +/* ------------------------------------------------------------------ + * IOCTL vidioc handling + * ------------------------------------------------------------------ + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strscpy(cap->driver, "tm6000", sizeof(cap->driver)); + strscpy(cap->card, "Trident TM5600/6000/6010", sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + if (dev->caps.has_radio) + cap->capabilities |= V4L2_CAP_RADIO; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(format)) + return -EINVAL; + + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, 2, "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + field = V4L2_FIELD_INTERLACED; + + tm6000_get_std_res(dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev, fh, false)) + return -EBUSY; + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) +{ + int rc = 0; + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + dev->norm = norm; + rc = tm6000_init_analog_mode(dev); + + fh->width = dev->width; + fh->height = dev->height; + + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); + + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *norm = dev->norm; + return 0; +} + +static const char *iname[] = { + [TM6000_INPUT_TV] = "Television", + [TM6000_INPUT_COMPOSITE1] = "Composite 1", + [TM6000_INPUT_COMPOSITE2] = "Composite 2", + [TM6000_INPUT_SVIDEO] = "S-Video", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= 3) + return -EINVAL; + + if (!dev->vinput[n].type) + return -EINVAL; + + i->index = n; + + if (dev->vinput[n].type == TM6000_INPUT_TV) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + strscpy(i->name, iname[dev->vinput[n].type], sizeof(i->name)); + + i->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *i = dev->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int rc = 0; + + if (i >= 3) + return -EINVAL; + if (!dev->vinput[i].type) + return -EINVAL; + + dev->input = i; + + rc = vidioc_s_std(file, priv, dev->norm); + + return rc; +} + +/* --- controls ---------------------------------------------- */ + +static int tm6000_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler); + u8 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); + return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops tm6000_ctrl_ops = { + .s_ctrl = tm6000_s_ctrl, +}; + +static int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tm6000_core *dev = container_of(ctrl->handler, + struct tm6000_core, radio_ctrl_handler); + u8 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = { + .s_ctrl = tm6000_radio_s_ctrl, +}; + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (0 != t->index) + return -EINVAL; + + strscpy(t->name, "Television", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + t->audmode = dev->amode; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (0 != t->index) + return -EINVAL; + + if (t->audmode > V4L2_TUNER_MODE_STEREO) + dev->amode = V4L2_TUNER_MODE_STEREO; + else + dev->amode = t->audmode; + dprintk(dev, 3, "audio mode: %x\n", t->audmode); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner) + return -EINVAL; + + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner != 0) + return -EINVAL; + + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strscpy(t->name, "Radio", sizeof(t->name)); + t->type = V4L2_TUNER_RADIO; + t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int __tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct tm6000_core *dev = video_drvdata(file); + struct tm6000_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int rc; + int radio = 0; + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + default: + return -EINVAL; + } + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[type], + dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + v4l2_fh_init(&fh->fh, vdev); + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + dev->radio = radio; + fh->type = type; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res(dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n", + fh, dev, &dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + /* initialize hardware on analog mode */ + rc = tm6000_init_analog_mode(dev); + if (rc < 0) { + v4l2_fh_exit(&fh->fh); + kfree(fh); + return rc; + } + + dev->mode = TM6000_MODE_ANALOG; + + if (!fh->radio) { + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer), fh, &dev->lock); + } else { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + tm6000_set_audio_rinput(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + v4l2_fh_add(&fh->fh); + + return 0; +} + +static int tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + int res; + + mutex_lock(vdev->lock); + res = __tm6000_open(file); + mutex_unlock(vdev->lock); + return res; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int res; + + if (!res_get(fh->dev, fh, true)) + return -EBUSY; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return res; + } + return 0; +} + +static __poll_t +__tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + __poll_t req_events = poll_requested_events(wait); + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + __poll_t res = 0; + + if (v4l2_event_pending(&fh->fh)) + res = EPOLLPRI; + else if (req_events & EPOLLPRI) + poll_wait(file, &fh->fh.wait, wait); + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return res | EPOLLERR; + + if (!!is_res_streaming(fh->dev, fh)) + return res | EPOLLERR; + + if (!is_res_read(fh->dev, fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return res | EPOLLERR; + buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return res | EPOLLIN | EPOLLRDNORM; + } else if (req_events & (EPOLLIN | EPOLLRDNORM)) { + /* read() capture */ + return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); + } + return res; +} + +static __poll_t tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + __poll_t res; + + mutex_lock(&dev->lock); + res = __tm6000_poll(file, wait); + mutex_unlock(&dev->lock); + return res; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + mutex_lock(&dev->lock); + dev->users--; + + res_free(dev, fh); + + if (!dev->users) { + tm6000_uninit_isoc(dev); + + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_reset_configuration(dev->udev); + + if (dev->int_in.endp) + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 2); + else + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 0); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + if (!fh->radio) + videobuf_mmap_free(&fh->vb_vidq); + } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + kfree(fh); + mutex_unlock(&dev->lock); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + int res; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + return res; +} + +static const struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TM6000_STD, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .poll = v4l2_ctrl_poll, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/* ----------------------------------------------------------------- + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + +static void vdev_init(struct tm6000_core *dev, + struct video_device *vfd, + const struct video_device + *template, const char *type_name) +{ + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release_empty; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); +} + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = 0; + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 6); + v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 54); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 119); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 112); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_add_handler(&dev->ctrl_handler, + &dev->radio_ctrl_handler, NULL, false); + + if (dev->radio_ctrl_handler.error) + ret = dev->radio_ctrl_handler.error; + if (!ret && dev->ctrl_handler.error) + ret = dev->ctrl_handler.error; + if (ret) + goto free_ctrl; + + vdev_init(dev, &dev->vfd, &tm6000_template, "video"); + + dev->vfd.ctrl_handler = &dev->ctrl_handler; + dev->vfd.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vfd.device_caps |= V4L2_CAP_TUNER; + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr); + + if (ret < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + goto free_ctrl; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(&dev->vfd)); + + if (dev->caps.has_radio) { + vdev_init(dev, &dev->radio_dev, &tm6000_radio_template, + "radio"); + dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler; + dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + goto unreg_video; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(&dev->radio_dev)); + } + + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); + return ret; + +unreg_video: + video_unregister_device(&dev->vfd); +free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + video_unregister_device(&dev->vfd); + + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + + video_unregister_device(&dev->radio_dev); + return 0; +} + +int tm6000_v4l2_exit(void) +{ + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "Allow changing video device number"); + +module_param_named(debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug, "activates debug info"); + +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000.h b/drivers/staging/media/deprecated/tm6000/tm6000.h new file mode 100644 index 000000000000..c08c95312739 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000.h @@ -0,0 +1,396 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab + * + * Copyright (c) 2007 Michel Ludwig + * - DVB-T support + */ + +#include +#include +#include +#include "tm6000-usb-isoc.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Inputs */ +enum tm6000_itype { + TM6000_INPUT_TV = 1, + TM6000_INPUT_COMPOSITE1, + TM6000_INPUT_COMPOSITE2, + TM6000_INPUT_SVIDEO, + TM6000_INPUT_DVB, + TM6000_INPUT_RADIO, +}; + +enum tm6000_mux { + TM6000_VMUX_VIDEO_A = 1, + TM6000_VMUX_VIDEO_B, + TM6000_VMUX_VIDEO_AB, + TM6000_AMUX_ADC1, + TM6000_AMUX_ADC2, + TM6000_AMUX_SIF1, + TM6000_AMUX_SIF2, + TM6000_AMUX_I2S, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +struct tm6000_input { + enum tm6000_itype type; + enum tm6000_mux vmux; + enum tm6000_mux amux; + unsigned int v_gpio; + unsigned int a_gpio; +}; + +/* ------------------------------------------------------------------ + * Basic structures + * ------------------------------------------------------------------ + */ + +struct tm6000_fmt { + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum tm6000_mode { + TM6000_MODE_UNKNOWN = 0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_gpio { + int tuner_reset; + int tuner_on; + int demod_reset; + int demod_on; + int power_led; + int dvb_led; + int ir; +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; + unsigned int has_radio:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct snd_tm6000_card { + struct snd_card *card; + spinlock_t reg_lock; + struct tm6000_core *core; + struct snd_pcm_substream *substream; + + /* temporary data for buffer fill processing */ + unsigned buf_pos; + unsigned period_pos; +}; + +struct tm6000_endpoint { + struct usb_host_endpoint *endp; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + unsigned maxsize; +}; + +#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + unsigned char eedata[256]; /* Eeprom data */ + unsigned eedata_size; /* Size of the eeprom info */ + + v4l2_std_id norm; /* Current norm */ + int width, height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Used to load alsa/dvb */ + struct work_struct request_module_wk; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + struct tm6000_gpio gpio; + + char *ir_codes; + + __u8 radio; + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + + /* extension */ + struct list_head devlist; + + /* video for linux */ + int users; + + /* various device info */ + struct tm6000_fh *resources; /* Points to fh that is streaming */ + bool is_res_read; + + struct video_device vfd; + struct video_device radio_dev; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; + + int input; + struct tm6000_input vinput[3]; /* video input */ + struct tm6000_input rinput; /* radio input */ + + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + int ctl_mute; /* audio */ + int ctl_volume; + int amode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* audio support */ + struct snd_tm6000_card *adev; + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct tm6000_IR *ir; + + /* locks */ + struct mutex lock; + struct mutex usb_lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + struct tm6000_endpoint int_in, int_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; + + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + + unsigned long quirks; +}; + +enum tm6000_ops_type { + TM6000_AUDIO = 0x10, + TM6000_DVB = 0x20, +}; + +struct tm6000_ops { + struct list_head next; + char *name; + enum tm6000_ops_type type; + int (*init)(struct tm6000_core *); + int (*fini)(struct tm6000_core *); + int (*fillbuf)(struct tm6000_core *, char *buf, int size); +}; + +struct tm6000_fh { + struct v4l2_fh fh; + struct tm6000_core *dev; + unsigned int radio; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width, height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) + +/* In tm6000-cards.c */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); +int tm6000_cards_setup(struct tm6000_core *dev); +void tm6000_flash_led(struct tm6000_core *dev, u8 state); + +/* In tm6000-core.c */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); +int tm6000_init(struct tm6000_core *dev); +int tm6000_reset(struct tm6000_core *dev); + +int tm6000_init_analog_mode(struct tm6000_core *dev); +int tm6000_init_digital_mode(struct tm6000_core *dev); +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_rinput(struct tm6000_core *dev); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +void tm6000_remove_from_devlist(struct tm6000_core *dev); +void tm6000_add_into_devlist(struct tm6000_core *dev); +int tm6000_register_extension(struct tm6000_ops *ops); +void tm6000_unregister_extension(struct tm6000_ops *ops); +void tm6000_init_extension(struct tm6000_core *dev); +void tm6000_close_extension(struct tm6000_core *dev); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size); + + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard(struct tm6000_core *dev); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +/* In tm6000-queue.c */ + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t *f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ + +/* In tm6000-input.c */ +int tm6000_ir_init(struct tm6000_core *dev); +int tm6000_ir_fini(struct tm6000_core *dev); +void tm6000_ir_wait(struct tm6000_core *dev, u8 state); +int tm6000_ir_int_start(struct tm6000_core *dev); +void tm6000_ir_int_stop(struct tm6000_core *dev); + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __func__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __func__ , ##arg); } while (0) -- cgit v1.3-14-g43fede From 43f2d33e2f2844dddeadee339a6e6e2e2638b8f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:46 +0200 Subject: media: fsl-viu: deprecate this driver Deprecate the fsl-viu driver. This driver does not use the vb2 framework for video streaming, instead it uses the old videobuf framework. We want to get rid of these old drivers, so deprecated it for future removal. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/nxp/Kconfig | 12 - drivers/media/platform/nxp/Makefile | 1 - drivers/media/platform/nxp/fsl-viu.c | 1599 -------------------- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/deprecated/fsl-viu/Kconfig | 15 + drivers/staging/media/deprecated/fsl-viu/Makefile | 2 + drivers/staging/media/deprecated/fsl-viu/TODO | 7 + drivers/staging/media/deprecated/fsl-viu/fsl-viu.c | 1599 ++++++++++++++++++++ 9 files changed, 1625 insertions(+), 1612 deletions(-) delete mode 100644 drivers/media/platform/nxp/fsl-viu.c create mode 100644 drivers/staging/media/deprecated/fsl-viu/Kconfig create mode 100644 drivers/staging/media/deprecated/fsl-viu/Makefile create mode 100644 drivers/staging/media/deprecated/fsl-viu/TODO create mode 100644 drivers/staging/media/deprecated/fsl-viu/fsl-viu.c diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig index 4c76656e353e..5917634889b5 100644 --- a/drivers/media/platform/nxp/Kconfig +++ b/drivers/media/platform/nxp/Kconfig @@ -15,18 +15,6 @@ config VIDEO_IMX_MIPI_CSIS Video4Linux2 sub-device driver for the MIPI CSI-2 CSIS receiver v3.3/v3.6.3 found on some i.MX7 and i.MX8 SoCs. -config VIDEO_VIU - tristate "NXP VIU Video Driver" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && (PPC_MPC512x || COMPILE_TEST) && I2C - select VIDEOBUF_DMA_CONTIG - help - Support for Freescale VIU video driver. This device captures - video data, or overlays video on DIU frame buffer. - - Say Y here if you want to enable VIU device on MPC5121e Rev2+. - In doubt, say N. - # mem2mem drivers config VIDEO_IMX_PXP diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile index 22ba28ac6d63..81ab304ef31c 100644 --- a/drivers/media/platform/nxp/Makefile +++ b/drivers/media/platform/nxp/Makefile @@ -6,4 +6,3 @@ obj-y += imx-jpeg/ obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o -obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o diff --git a/drivers/media/platform/nxp/fsl-viu.c b/drivers/media/platform/nxp/fsl-viu.c deleted file mode 100644 index afc96f6db2a1..000000000000 --- a/drivers/media/platform/nxp/fsl-viu.c +++ /dev/null @@ -1,1599 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * - * Freescale VIU video driver - * - * Authors: Hongjun Chen - * Porting to 2.6.35 by DENX Software Engineering, - * Anatolij Gustschin - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "fsl_viu" -#define VIU_VERSION "0.5.1" - -#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ - -#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ - -/* I2C address of video decoder chip is 0x4A */ -#define VIU_VIDEO_DECODER_ADDR 0x25 - -static int info_level; - -#define dprintk(level, fmt, arg...) \ - do { \ - if (level <= info_level) \ - printk(KERN_DEBUG "viu: " fmt , ## arg); \ - } while (0) - -/* - * Basic structures - */ -struct viu_fmt { - u32 fourcc; /* v4l2 format id */ - u32 pixelformat; - int depth; -}; - -static struct viu_fmt formats[] = { - { - .fourcc = V4L2_PIX_FMT_RGB565, - .pixelformat = V4L2_PIX_FMT_RGB565, - .depth = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, - .pixelformat = V4L2_PIX_FMT_RGB32, - .depth = 32, - } -}; - -struct viu_dev; -struct viu_buf; - -/* buffer for one video frame */ -struct viu_buf { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - struct viu_fmt *fmt; -}; - -struct viu_dmaqueue { - struct viu_dev *dev; - struct list_head active; - struct list_head queued; - struct timer_list timeout; -}; - -struct viu_status { - u32 field_irq; - u32 vsync_irq; - u32 hsync_irq; - u32 vstart_irq; - u32 dma_end_irq; - u32 error_irq; -}; - -struct viu_reg { - u32 status_cfg; - u32 luminance; - u32 chroma_r; - u32 chroma_g; - u32 chroma_b; - u32 field_base_addr; - u32 dma_inc; - u32 picture_count; - u32 req_alarm; - u32 alpha; -} __attribute__ ((packed)); - -struct viu_dev { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct mutex lock; - spinlock_t slock; - int users; - - struct device *dev; - /* various device info */ - struct video_device *vdev; - struct viu_dmaqueue vidq; - enum v4l2_field capfield; - int field; - int first; - int dma_done; - - /* Hardware register area */ - struct viu_reg __iomem *vr; - - /* Interrupt vector */ - int irq; - struct viu_status irqs; - - /* video overlay */ - struct v4l2_framebuffer ovbuf; - struct viu_fmt *ovfmt; - unsigned int ovenable; - enum v4l2_field ovfield; - - /* crop */ - struct v4l2_rect crop_current; - - /* clock pointer */ - struct clk *clk; - - /* decoder */ - struct v4l2_subdev *decoder; - - v4l2_std_id std; -}; - -struct viu_fh { - /* must remain the first field of this struct */ - struct v4l2_fh fh; - struct viu_dev *dev; - - /* video capture */ - struct videobuf_queue vb_vidq; - spinlock_t vbq_lock; /* spinlock for the videobuf queue */ - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip clips[1]; - - /* video capture */ - struct viu_fmt *fmt; - int width, height, sizeimage; - enum v4l2_buf_type type; -}; - -static struct viu_reg reg_val; - -/* - * Macro definitions of VIU registers - */ - -/* STATUS_CONFIG register */ -enum status_config { - SOFT_RST = 1 << 0, - - ERR_MASK = 0x0f << 4, /* Error code mask */ - ERR_NO = 0x00, /* No error */ - ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ - ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ - ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ - ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ - ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ - ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ - ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ - ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ - ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ - ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ - - INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ - INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ - INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ - INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ - INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ - INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ - INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ - - INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ - INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ - INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ - INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ - INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ - INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ - - DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ - FIELD_NO = 0x01 << 28, /* Field number */ - DITHER_ON = 0x01 << 29, /* Dithering is on */ - ROUND_ON = 0x01 << 30, /* Round is on */ - MODE_32BIT = 1UL << 31, /* Data in RGBa888, - * 0 in RGB565 - */ -}; - -#define norm_maxw() 720 -#define norm_maxh() 576 - -#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ - INT_HSYNC_STATUS | INT_VSTART_STATUS | \ - INT_DMA_END_STATUS | INT_ERROR_STATUS) - -#define NUM_FORMATS ARRAY_SIZE(formats) - -static irqreturn_t viu_intr(int irq, void *dev_id); - -static struct viu_fmt *format_by_fourcc(int fourcc) -{ - int i; - - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].pixelformat == fourcc) - return formats + i; - } - - dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); - return NULL; -} - -static void viu_start_dma(struct viu_dev *dev) -{ - struct viu_reg __iomem *vr = dev->vr; - - dev->field = 0; - - /* Enable DMA operation */ - iowrite32be(SOFT_RST, &vr->status_cfg); - iowrite32be(INT_FIELD_EN, &vr->status_cfg); -} - -static void viu_stop_dma(struct viu_dev *dev) -{ - struct viu_reg __iomem *vr = dev->vr; - int cnt = 100; - u32 status_cfg; - - iowrite32be(0, &vr->status_cfg); - - /* Clear pending interrupts */ - status_cfg = ioread32be(&vr->status_cfg); - if (status_cfg & 0x3f0000) - iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); - - if (status_cfg & DMA_ACT) { - do { - status_cfg = ioread32be(&vr->status_cfg); - if (status_cfg & INT_DMA_END_STATUS) - break; - } while (cnt--); - - if (cnt < 0) { - /* timed out, issue soft reset */ - iowrite32be(SOFT_RST, &vr->status_cfg); - iowrite32be(0, &vr->status_cfg); - } else { - /* clear DMA_END and other pending irqs */ - iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); - } - } - - dev->field = 0; -} - -static int restart_video_queue(struct viu_dmaqueue *vidq) -{ - struct viu_buf *buf, *prev; - - dprintk(1, "%s vidq=%p\n", __func__, vidq); - if (!list_empty(&vidq->active)) { - buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - - viu_stop_dma(vidq->dev); - - /* cancel all outstanding capture requests */ - list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - } - mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&vidq->queued)) - return 0; - buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); - if (prev == NULL) { - list_move_tail(&buf->vb.queue, &vidq->active); - - dprintk(1, "Restarting video dma\n"); - viu_stop_dma(vidq->dev); - viu_start_dma(vidq->dev); - - buf->vb.state = VIDEOBUF_ACTIVE; - mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] restart_queue - first active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &vidq->active); - buf->vb.state = VIDEOBUF_ACTIVE; - dprintk(2, "[%p/%d] restart_queue - move to active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } -} - -static void viu_vid_timeout(struct timer_list *t) -{ - struct viu_dev *dev = from_timer(dev, t, vidq.timeout); - struct viu_buf *buf; - struct viu_dmaqueue *vidq = &dev->vidq; - - while (!list_empty(&vidq->active)) { - buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); - } - - restart_video_queue(vidq); -} - -/* - * Videobuf operations - */ -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct viu_fh *fh = vq->priv_data; - - *size = fh->width * fh->height * fh->fmt->depth >> 3; - if (*count == 0) - *count = 32; - - while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) - (*count)--; - - dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) -{ - struct videobuf_buffer *vb = &buf->vb; - void *vaddr = NULL; - - videobuf_waiton(vq, &buf->vb, 0, 0); - - if (vq->int_ops && vq->int_ops->vaddr) - vaddr = vq->int_ops->vaddr(vb); - - if (vaddr) - videobuf_dma_contig_free(vq, &buf->vb); - - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) -{ - struct viu_reg __iomem *vr = dev->vr; - int bpp; - - /* setup the DMA base address */ - reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); - - dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", - buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); - - /* interlace is on by default, set horizontal DMA increment */ - reg_val.status_cfg = 0; - bpp = buf->fmt->depth >> 3; - switch (bpp) { - case 2: - reg_val.status_cfg &= ~MODE_32BIT; - reg_val.dma_inc = buf->vb.width * 2; - break; - case 4: - reg_val.status_cfg |= MODE_32BIT; - reg_val.dma_inc = buf->vb.width * 4; - break; - default: - dprintk(0, "doesn't support color depth(%d)\n", - bpp * 8); - return -EINVAL; - } - - /* setup picture_count register */ - reg_val.picture_count = (buf->vb.height / 2) << 16 | - buf->vb.width; - - reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; - - buf->vb.state = VIDEOBUF_ACTIVE; - dev->capfield = buf->vb.field; - - /* reset dma increment if needed */ - if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) - reg_val.dma_inc = 0; - - iowrite32be(reg_val.dma_inc, &vr->dma_inc); - iowrite32be(reg_val.picture_count, &vr->picture_count); - iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); - mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); - return 0; -} - -static int buffer_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct viu_fh *fh = vq->priv_data; - struct viu_buf *buf = container_of(vb, struct viu_buf, vb); - int rc; - - BUG_ON(fh->fmt == NULL); - - if (fh->width < 48 || fh->width > norm_maxw() || - fh->height < 32 || fh->height > norm_maxh()) - return -EINVAL; - buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; - if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - } - - if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc != 0) - goto fail; - - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - buf->fmt = fh->fmt; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct viu_buf *buf = container_of(vb, struct viu_buf, vb); - struct viu_fh *fh = vq->priv_data; - struct viu_dev *dev = fh->dev; - struct viu_dmaqueue *vidq = &dev->vidq; - struct viu_buf *prev; - - if (!list_empty(&vidq->queued)) { - dprintk(1, "adding vb queue=%p\n", &buf->vb.queue); - dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", - vidq, &vidq->queued); - dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", - dev, &vidq->queued, vidq->queued.next, - vidq->queued.prev); - list_add_tail(&buf->vb.queue, &vidq->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", - buf, buf->vb.i); - } else if (list_empty(&vidq->active)) { - dprintk(1, "adding vb active=%p\n", &buf->vb.queue); - list_add_tail(&buf->vb.queue, &vidq->active); - buf->vb.state = VIDEOBUF_ACTIVE; - mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] buffer_queue - first active\n", - buf, buf->vb.i); - - buffer_activate(dev, buf); - } else { - dprintk(1, "adding vb queue2=%p\n", &buf->vb.queue); - prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); - if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &vidq->active); - buf->vb.state = VIDEOBUF_ACTIVE; - dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); - } else { - list_add_tail(&buf->vb.queue, &vidq->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", - buf, buf->vb.i); - } - } -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct viu_buf *buf = container_of(vb, struct viu_buf, vb); - struct viu_fh *fh = vq->priv_data; - struct viu_dev *dev = (struct viu_dev *)fh->dev; - - viu_stop_dma(dev); - free_buffer(vq, buf); -} - -static const struct videobuf_queue_ops viu_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* - * IOCTL vidioc handling - */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "viu", sizeof(cap->driver)); - strscpy(cap->card, "viu", sizeof(cap->card)); - strscpy(cap->bus_info, "platform:viu", sizeof(cap->bus_info)); - return 0; -} - -static int vidioc_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int index = f->index; - - if (f->index >= NUM_FORMATS) - return -EINVAL; - - f->pixelformat = formats[index].fourcc; - return 0; -} - -static int vidioc_g_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct viu_fh *fh = priv; - - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->pixelformat; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = fh->sizeimage; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int vidioc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct viu_fmt *fmt; - unsigned int maxw, maxh; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) { - dprintk(1, "Fourcc format (0x%08x) invalid.", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - maxw = norm_maxw(); - maxh = norm_maxh(); - - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int vidioc_s_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct viu_fh *fh = priv; - int ret; - - ret = vidioc_try_fmt_cap(file, fh, f); - if (ret < 0) - return ret; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->sizeimage = f->fmt.pix.sizeimage; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - return 0; -} - -static int vidioc_g_fmt_overlay(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct viu_fh *fh = priv; - - f->fmt.win = fh->win; - return 0; -} - -static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) -{ - enum v4l2_field field; - int maxw, maxh; - - if (dev->ovbuf.base == NULL) - return -EINVAL; - if (dev->ovfmt == NULL) - return -EINVAL; - if (win->w.width < 48 || win->w.height < 32) - return -EINVAL; - - field = win->field; - maxw = dev->crop_current.width; - maxh = dev->crop_current.height; - - if (field == V4L2_FIELD_ANY) { - field = (win->w.height > maxh/2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_TOP; - } - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } - - win->field = field; - if (win->w.width > maxw) - win->w.width = maxw; - if (win->w.height > maxh) - win->w.height = maxh; - return 0; -} - -inline void viu_activate_overlay(struct viu_reg __iomem *vr) -{ - iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); - iowrite32be(reg_val.dma_inc, &vr->dma_inc); - iowrite32be(reg_val.picture_count, &vr->picture_count); -} - -static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) -{ - int bpp; - - dprintk(1, "%s %dx%d\n", __func__, - fh->win.w.width, fh->win.w.height); - - reg_val.status_cfg = 0; - - /* setup window */ - reg_val.picture_count = (fh->win.w.height / 2) << 16 | - fh->win.w.width; - - /* setup color depth and dma increment */ - bpp = dev->ovfmt->depth / 8; - switch (bpp) { - case 2: - reg_val.status_cfg &= ~MODE_32BIT; - reg_val.dma_inc = fh->win.w.width * 2; - break; - case 4: - reg_val.status_cfg |= MODE_32BIT; - reg_val.dma_inc = fh->win.w.width * 4; - break; - default: - dprintk(0, "device doesn't support color depth(%d)\n", - bpp * 8); - return -EINVAL; - } - - dev->ovfield = fh->win.field; - if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) - reg_val.dma_inc = 0; - - reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; - - /* setup the base address of the overlay buffer */ - reg_val.field_base_addr = (u32)(long)dev->ovbuf.base; - - return 0; -} - -static int vidioc_s_fmt_overlay(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct viu_fh *fh = priv; - struct viu_dev *dev = (struct viu_dev *)fh->dev; - unsigned long flags; - int err; - - err = verify_preview(dev, &f->fmt.win); - if (err) - return err; - - fh->win = f->fmt.win; - - spin_lock_irqsave(&dev->slock, flags); - viu_setup_preview(dev, fh); - spin_unlock_irqrestore(&dev->slock, flags); - return 0; -} - -static int vidioc_try_fmt_overlay(struct file *file, void *priv, - struct v4l2_format *f) -{ - return 0; -} - -static int vidioc_overlay(struct file *file, void *priv, unsigned int on) -{ - struct viu_fh *fh = priv; - struct viu_dev *dev = (struct viu_dev *)fh->dev; - unsigned long flags; - - if (on) { - spin_lock_irqsave(&dev->slock, flags); - viu_activate_overlay(dev->vr); - dev->ovenable = 1; - - /* start dma */ - viu_start_dma(dev); - spin_unlock_irqrestore(&dev->slock, flags); - } else { - viu_stop_dma(dev); - dev->ovenable = 0; - } - - return 0; -} - -static int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) -{ - struct viu_fh *fh = priv; - struct viu_dev *dev = fh->dev; - struct v4l2_framebuffer *fb = arg; - - *fb = dev->ovbuf; - fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - return 0; -} - -static int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) -{ - struct viu_fh *fh = priv; - struct viu_dev *dev = fh->dev; - const struct v4l2_framebuffer *fb = arg; - struct viu_fmt *fmt; - - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) - return -EPERM; - - /* check args */ - fmt = format_by_fourcc(fb->fmt.pixelformat); - if (fmt == NULL) - return -EINVAL; - - /* ok, accept it */ - dev->ovbuf = *fb; - dev->ovfmt = fmt; - if (dev->ovbuf.fmt.bytesperline == 0) { - dev->ovbuf.fmt.bytesperline = - dev->ovbuf.fmt.width * fmt->depth / 8; - } - return 0; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct viu_fh *fh = priv; - - return videobuf_reqbufs(&fh->vb_vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct viu_fh *fh = priv; - - return videobuf_querybuf(&fh->vb_vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct viu_fh *fh = priv; - - return videobuf_qbuf(&fh->vb_vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct viu_fh *fh = priv; - - return videobuf_dqbuf(&fh->vb_vidq, p, - file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct viu_fh *fh = priv; - struct viu_dev *dev = fh->dev; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (fh->type != i) - return -EINVAL; - - if (dev->ovenable) - dev->ovenable = 0; - - viu_start_dma(fh->dev); - - return videobuf_streamon(&fh->vb_vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct viu_fh *fh = priv; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (fh->type != i) - return -EINVAL; - - viu_stop_dma(fh->dev); - - return videobuf_streamoff(&fh->vb_vidq); -} - -#define decoder_call(viu, o, f, args...) \ - v4l2_subdev_call(viu->decoder, o, f, ##args) - -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) -{ - struct viu_fh *fh = priv; - - decoder_call(fh->dev, video, querystd, std_id); - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) -{ - struct viu_fh *fh = priv; - - fh->dev->std = id; - decoder_call(fh->dev, video, s_std, id); - return 0; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id) -{ - struct viu_fh *fh = priv; - - *std_id = fh->dev->std; - return 0; -} - -/* only one input in this driver */ -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct viu_fh *fh = priv; - - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = fh->dev->vdev->tvnorms; - strscpy(inp->name, "Camera", sizeof(inp->name)); - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct viu_fh *fh = priv; - - if (i) - return -EINVAL; - - decoder_call(fh->dev, video, s_routing, i, 0, 0); - return 0; -} - -inline void viu_activate_next_buf(struct viu_dev *dev, - struct viu_dmaqueue *viuq) -{ - struct viu_dmaqueue *vidq = viuq; - struct viu_buf *buf; - - /* launch another DMA operation for an active/queued buffer */ - if (!list_empty(&vidq->active)) { - buf = list_entry(vidq->active.next, struct viu_buf, - vb.queue); - dprintk(1, "start another queued buffer: 0x%p\n", buf); - buffer_activate(dev, buf); - } else if (!list_empty(&vidq->queued)) { - buf = list_entry(vidq->queued.next, struct viu_buf, - vb.queue); - list_del(&buf->vb.queue); - - dprintk(1, "start another queued buffer: 0x%p\n", buf); - list_add_tail(&buf->vb.queue, &vidq->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buffer_activate(dev, buf); - } -} - -inline void viu_default_settings(struct viu_reg __iomem *vr) -{ - iowrite32be(0x9512A254, &vr->luminance); - iowrite32be(0x03310000, &vr->chroma_r); - iowrite32be(0x06600F38, &vr->chroma_g); - iowrite32be(0x00000409, &vr->chroma_b); - iowrite32be(0x000000ff, &vr->alpha); - iowrite32be(0x00000090, &vr->req_alarm); - dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", - ioread32be(&vr->status_cfg), ioread32be(&vr->field_base_addr)); -} - -static void viu_overlay_intr(struct viu_dev *dev, u32 status) -{ - struct viu_reg __iomem *vr = dev->vr; - - if (status & INT_DMA_END_STATUS) - dev->dma_done = 1; - - if (status & INT_FIELD_STATUS) { - if (dev->dma_done) { - u32 addr = reg_val.field_base_addr; - - dev->dma_done = 0; - if (status & FIELD_NO) - addr += reg_val.dma_inc; - - iowrite32be(addr, &vr->field_base_addr); - iowrite32be(reg_val.dma_inc, &vr->dma_inc); - iowrite32be((status & 0xffc0ffff) | - (status & INT_ALL_STATUS) | - reg_val.status_cfg, &vr->status_cfg); - } else if (status & INT_VSYNC_STATUS) { - iowrite32be((status & 0xffc0ffff) | - (status & INT_ALL_STATUS) | - reg_val.status_cfg, &vr->status_cfg); - } - } -} - -static void viu_capture_intr(struct viu_dev *dev, u32 status) -{ - struct viu_dmaqueue *vidq = &dev->vidq; - struct viu_reg __iomem *vr = dev->vr; - struct viu_buf *buf; - int field_num; - int need_two; - int dma_done = 0; - - field_num = status & FIELD_NO; - need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); - - if (status & INT_DMA_END_STATUS) { - dma_done = 1; - if (((field_num == 0) && (dev->field == 0)) || - (field_num && (dev->field == 1))) - dev->field++; - } - - if (status & INT_FIELD_STATUS) { - dprintk(1, "irq: field %d, done %d\n", - !!field_num, dma_done); - if (unlikely(dev->first)) { - if (field_num == 0) { - dev->first = 0; - dprintk(1, "activate first buf\n"); - viu_activate_next_buf(dev, vidq); - } else - dprintk(1, "wait field 0\n"); - return; - } - - /* setup buffer address for next dma operation */ - if (!list_empty(&vidq->active)) { - u32 addr = reg_val.field_base_addr; - - if (field_num && need_two) { - addr += reg_val.dma_inc; - dprintk(1, "field 1, 0x%lx, dev field %d\n", - (unsigned long)addr, dev->field); - } - iowrite32be(addr, &vr->field_base_addr); - iowrite32be(reg_val.dma_inc, &vr->dma_inc); - iowrite32be((status & 0xffc0ffff) | - (status & INT_ALL_STATUS) | - reg_val.status_cfg, &vr->status_cfg); - return; - } - } - - if (dma_done && field_num && (dev->field == 2)) { - dev->field = 0; - buf = list_entry(vidq->active.next, - struct viu_buf, vb.queue); - dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", - buf, buf->vb.i, - (unsigned long)videobuf_to_dma_contig(&buf->vb), - (unsigned long)ioread32be(&vr->field_base_addr)); - - if (waitqueue_active(&buf->vb.done)) { - list_del(&buf->vb.queue); - buf->vb.ts = ktime_get_ns(); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - wake_up(&buf->vb.done); - } - /* activate next dma buffer */ - viu_activate_next_buf(dev, vidq); - } -} - -static irqreturn_t viu_intr(int irq, void *dev_id) -{ - struct viu_dev *dev = (struct viu_dev *)dev_id; - struct viu_reg __iomem *vr = dev->vr; - u32 status; - u32 error; - - status = ioread32be(&vr->status_cfg); - - if (status & INT_ERROR_STATUS) { - dev->irqs.error_irq++; - error = status & ERR_MASK; - if (error) - dprintk(1, "Err: error(%d), times:%d!\n", - error >> 4, dev->irqs.error_irq); - /* Clear interrupt error bit and error flags */ - iowrite32be((status & 0xffc0ffff) | INT_ERROR_STATUS, - &vr->status_cfg); - } - - if (status & INT_DMA_END_STATUS) { - dev->irqs.dma_end_irq++; - dev->dma_done = 1; - dprintk(2, "VIU DMA end interrupt times: %d\n", - dev->irqs.dma_end_irq); - } - - if (status & INT_HSYNC_STATUS) - dev->irqs.hsync_irq++; - - if (status & INT_FIELD_STATUS) { - dev->irqs.field_irq++; - dprintk(2, "VIU field interrupt times: %d\n", - dev->irqs.field_irq); - } - - if (status & INT_VSTART_STATUS) - dev->irqs.vstart_irq++; - - if (status & INT_VSYNC_STATUS) { - dev->irqs.vsync_irq++; - dprintk(2, "VIU vsync interrupt times: %d\n", - dev->irqs.vsync_irq); - } - - /* clear all pending irqs */ - status = ioread32be(&vr->status_cfg); - iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS), - &vr->status_cfg); - - if (dev->ovenable) { - viu_overlay_intr(dev, status); - return IRQ_HANDLED; - } - - /* Capture mode */ - viu_capture_intr(dev, status); - return IRQ_HANDLED; -} - -/* - * File operations for the device - */ -static int viu_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct viu_dev *dev = video_get_drvdata(vdev); - struct viu_fh *fh; - struct viu_reg __iomem *vr; - int minor = vdev->minor; - u32 status_cfg; - - dprintk(1, "viu: open (minor=%d)\n", minor); - - dev->users++; - if (dev->users > 1) { - dev->users--; - return -EBUSY; - } - - vr = dev->vr; - - dprintk(1, "open minor=%d type=%s users=%d\n", minor, - v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); - - if (mutex_lock_interruptible(&dev->lock)) { - dev->users--; - return -ERESTARTSYS; - } - - /* allocate and initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) { - dev->users--; - mutex_unlock(&dev->lock); - return -ENOMEM; - } - - v4l2_fh_init(&fh->fh, vdev); - file->private_data = fh; - fh->dev = dev; - - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); - fh->width = norm_maxw(); - fh->height = norm_maxh(); - dev->crop_current.width = fh->width; - dev->crop_current.height = fh->height; - - dprintk(1, "Open: fh=%p, dev=%p, dev->vidq=%p\n", fh, dev, &dev->vidq); - dprintk(1, "Open: list_empty queued=%d\n", - list_empty(&dev->vidq.queued)); - dprintk(1, "Open: list_empty active=%d\n", - list_empty(&dev->vidq.active)); - - viu_default_settings(vr); - - status_cfg = ioread32be(&vr->status_cfg); - iowrite32be(status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | - INT_FIELD_EN | INT_VSTART_EN | - INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN), - &vr->status_cfg); - - status_cfg = ioread32be(&vr->status_cfg); - iowrite32be(status_cfg | INT_ALL_STATUS, &vr->status_cfg); - - spin_lock_init(&fh->vbq_lock); - videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, - dev->dev, &fh->vbq_lock, - fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct viu_buf), fh, - &fh->dev->lock); - v4l2_fh_add(&fh->fh); - mutex_unlock(&dev->lock); - return 0; -} - -static ssize_t viu_read(struct file *file, char __user *data, size_t count, - loff_t *ppos) -{ - struct viu_fh *fh = file->private_data; - struct viu_dev *dev = fh->dev; - int ret = 0; - - dprintk(2, "%s\n", __func__); - if (dev->ovenable) - dev->ovenable = 0; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - viu_start_dma(dev); - ret = videobuf_read_stream(&fh->vb_vidq, data, count, - ppos, 0, file->f_flags & O_NONBLOCK); - mutex_unlock(&dev->lock); - return ret; - } - return 0; -} - -static __poll_t viu_poll(struct file *file, struct poll_table_struct *wait) -{ - struct viu_fh *fh = file->private_data; - struct videobuf_queue *q = &fh->vb_vidq; - struct viu_dev *dev = fh->dev; - __poll_t req_events = poll_requested_events(wait); - __poll_t res = v4l2_ctrl_poll(file, wait); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return EPOLLERR; - - if (!(req_events & (EPOLLIN | EPOLLRDNORM))) - return res; - - mutex_lock(&dev->lock); - res |= videobuf_poll_stream(file, q, wait); - mutex_unlock(&dev->lock); - return res; -} - -static int viu_release(struct file *file) -{ - struct viu_fh *fh = file->private_data; - struct viu_dev *dev = fh->dev; - int minor = video_devdata(file)->minor; - - mutex_lock(&dev->lock); - viu_stop_dma(dev); - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - mutex_unlock(&dev->lock); - - kfree(fh); - - dev->users--; - dprintk(1, "close (minor=%d, users=%d)\n", - minor, dev->users); - return 0; -} - -static void viu_reset(struct viu_reg __iomem *reg) -{ - iowrite32be(0, ®->status_cfg); - iowrite32be(0x9512a254, ®->luminance); - iowrite32be(0x03310000, ®->chroma_r); - iowrite32be(0x06600f38, ®->chroma_g); - iowrite32be(0x00000409, ®->chroma_b); - iowrite32be(0, ®->field_base_addr); - iowrite32be(0, ®->dma_inc); - iowrite32be(0x01e002d0, ®->picture_count); - iowrite32be(0x00000090, ®->req_alarm); - iowrite32be(0x000000ff, ®->alpha); -} - -static int viu_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct viu_fh *fh = file->private_data; - struct viu_dev *dev = fh->dev; - int ret; - - dprintk(1, "mmap called, vma=%p\n", vma); - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - - dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - ret); - - return ret; -} - -static const struct v4l2_file_operations viu_fops = { - .owner = THIS_MODULE, - .open = viu_open, - .release = viu_release, - .read = viu_read, - .poll = viu_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = viu_mmap, -}; - -static const struct v4l2_ioctl_ops viu_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, - .vidioc_overlay = vidioc_overlay, - .vidioc_g_fbuf = vidioc_g_fbuf, - .vidioc_s_fbuf = vidioc_s_fbuf, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device viu_template = { - .name = "FSL viu", - .fops = &viu_fops, - .minor = -1, - .ioctl_ops = &viu_ioctl_ops, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE, -}; - -static int viu_of_probe(struct platform_device *op) -{ - struct viu_dev *viu_dev; - struct video_device *vdev; - struct resource r; - struct viu_reg __iomem *viu_regs; - struct i2c_adapter *ad; - int ret, viu_irq; - struct clk *clk; - - ret = of_address_to_resource(op->dev.of_node, 0, &r); - if (ret) { - dev_err(&op->dev, "Can't parse device node resource\n"); - return -ENODEV; - } - - viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (!viu_irq) { - dev_err(&op->dev, "Error while mapping the irq\n"); - return -EINVAL; - } - - /* request mem region */ - if (!devm_request_mem_region(&op->dev, r.start, - sizeof(struct viu_reg), DRV_NAME)) { - dev_err(&op->dev, "Error while requesting mem region\n"); - ret = -EBUSY; - goto err_irq; - } - - /* remap registers */ - viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); - if (!viu_regs) { - dev_err(&op->dev, "Can't map register set\n"); - ret = -ENOMEM; - goto err_irq; - } - - /* Prepare our private structure */ - viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_KERNEL); - if (!viu_dev) { - dev_err(&op->dev, "Can't allocate private structure\n"); - ret = -ENOMEM; - goto err_irq; - } - - viu_dev->vr = viu_regs; - viu_dev->irq = viu_irq; - viu_dev->dev = &op->dev; - - /* init video dma queues */ - INIT_LIST_HEAD(&viu_dev->vidq.active); - INIT_LIST_HEAD(&viu_dev->vidq.queued); - - snprintf(viu_dev->v4l2_dev.name, - sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); - ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); - if (ret < 0) { - dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); - goto err_irq; - } - - ad = i2c_get_adapter(0); - if (!ad) { - ret = -EFAULT; - dev_err(&op->dev, "couldn't get i2c adapter\n"); - goto err_v4l2; - } - - v4l2_ctrl_handler_init(&viu_dev->hdl, 5); - if (viu_dev->hdl.error) { - ret = viu_dev->hdl.error; - dev_err(&op->dev, "couldn't register control\n"); - goto err_i2c; - } - /* This control handler will inherit the control(s) from the - sub-device(s). */ - viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl; - viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, - "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); - - timer_setup(&viu_dev->vidq.timeout, viu_vid_timeout, 0); - viu_dev->std = V4L2_STD_NTSC_M; - viu_dev->first = 1; - - /* Allocate memory for video device */ - vdev = video_device_alloc(); - if (vdev == NULL) { - ret = -ENOMEM; - goto err_hdl; - } - - *vdev = viu_template; - - vdev->v4l2_dev = &viu_dev->v4l2_dev; - - viu_dev->vdev = vdev; - - /* initialize locks */ - mutex_init(&viu_dev->lock); - viu_dev->vdev->lock = &viu_dev->lock; - spin_lock_init(&viu_dev->slock); - - video_set_drvdata(viu_dev->vdev, viu_dev); - - mutex_lock(&viu_dev->lock); - - ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - video_device_release(viu_dev->vdev); - goto err_unlock; - } - - /* enable VIU clock */ - clk = devm_clk_get(&op->dev, "ipg"); - if (IS_ERR(clk)) { - dev_err(&op->dev, "failed to lookup the clock!\n"); - ret = PTR_ERR(clk); - goto err_vdev; - } - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(&op->dev, "failed to enable the clock!\n"); - goto err_vdev; - } - viu_dev->clk = clk; - - /* reset VIU module */ - viu_reset(viu_dev->vr); - - /* install interrupt handler */ - if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { - dev_err(&op->dev, "Request VIU IRQ failed.\n"); - ret = -ENODEV; - goto err_clk; - } - - mutex_unlock(&viu_dev->lock); - - dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); - return ret; - -err_clk: - clk_disable_unprepare(viu_dev->clk); -err_vdev: - video_unregister_device(viu_dev->vdev); -err_unlock: - mutex_unlock(&viu_dev->lock); -err_hdl: - v4l2_ctrl_handler_free(&viu_dev->hdl); -err_i2c: - i2c_put_adapter(ad); -err_v4l2: - v4l2_device_unregister(&viu_dev->v4l2_dev); -err_irq: - irq_dispose_mapping(viu_irq); - return ret; -} - -static int viu_of_remove(struct platform_device *op) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(op); - struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); - struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, - struct v4l2_subdev, list); - struct i2c_client *client = v4l2_get_subdevdata(sdev); - - free_irq(dev->irq, (void *)dev); - irq_dispose_mapping(dev->irq); - - clk_disable_unprepare(dev->clk); - - v4l2_ctrl_handler_free(&dev->hdl); - video_unregister_device(dev->vdev); - i2c_put_adapter(client->adapter); - v4l2_device_unregister(&dev->v4l2_dev); - return 0; -} - -#ifdef CONFIG_PM -static int viu_suspend(struct platform_device *op, pm_message_t state) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(op); - struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); - - clk_disable(dev->clk); - return 0; -} - -static int viu_resume(struct platform_device *op) -{ - struct v4l2_device *v4l2_dev = platform_get_drvdata(op); - struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); - - clk_enable(dev->clk); - return 0; -} -#endif - -/* - * Initialization and module stuff - */ -static const struct of_device_id mpc512x_viu_of_match[] = { - { - .compatible = "fsl,mpc5121-viu", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); - -static struct platform_driver viu_of_platform_driver = { - .probe = viu_of_probe, - .remove = viu_of_remove, -#ifdef CONFIG_PM - .suspend = viu_suspend, - .resume = viu_resume, -#endif - .driver = { - .name = DRV_NAME, - .of_match_table = mpc512x_viu_of_match, - }, -}; - -module_platform_driver(viu_of_platform_driver); - -MODULE_DESCRIPTION("Freescale Video-In(VIU)"); -MODULE_AUTHOR("Hongjun Chen"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(VIU_VERSION); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 0a0e052e39b1..15e92b3c6342 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -56,6 +56,7 @@ menuconfig STAGING_MEDIA_DEPRECATED if STAGING_MEDIA_DEPRECATED source "drivers/staging/media/deprecated/cpia2/Kconfig" +source "drivers/staging/media/deprecated/fsl-viu/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" source "drivers/staging/media/deprecated/tm6000/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index b7df6302dd8e..10f7844b6681 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ +obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ obj-$(CONFIG_DVB_AV7110) += av7110/ diff --git a/drivers/staging/media/deprecated/fsl-viu/Kconfig b/drivers/staging/media/deprecated/fsl-viu/Kconfig new file mode 100644 index 000000000000..399892c69a18 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_VIU + tristate "NXP VIU Video Driver (DEPRECATED)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && (PPC_MPC512x || COMPILE_TEST) && I2C + select VIDEOBUF_DMA_CONTIG + help + Support for Freescale VIU video driver. This device captures + video data, or overlays video on DIU frame buffer. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y here if you want to enable VIU device on MPC5121e Rev2+. + In doubt, say N. diff --git a/drivers/staging/media/deprecated/fsl-viu/Makefile b/drivers/staging/media/deprecated/fsl-viu/Makefile new file mode 100644 index 000000000000..931ec56ad08c --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o diff --git a/drivers/staging/media/deprecated/fsl-viu/TODO b/drivers/staging/media/deprecated/fsl-viu/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c b/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c new file mode 100644 index 000000000000..afc96f6db2a1 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c @@ -0,0 +1,1599 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Freescale VIU video driver + * + * Authors: Hongjun Chen + * Porting to 2.6.35 by DENX Software Engineering, + * Anatolij Gustschin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "fsl_viu" +#define VIU_VERSION "0.5.1" + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ + +/* I2C address of video decoder chip is 0x4A */ +#define VIU_VIDEO_DECODER_ADDR 0x25 + +static int info_level; + +#define dprintk(level, fmt, arg...) \ + do { \ + if (level <= info_level) \ + printk(KERN_DEBUG "viu: " fmt , ## arg); \ + } while (0) + +/* + * Basic structures + */ +struct viu_fmt { + u32 fourcc; /* v4l2 format id */ + u32 pixelformat; + int depth; +}; + +static struct viu_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .pixelformat = V4L2_PIX_FMT_RGB565, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, + .pixelformat = V4L2_PIX_FMT_RGB32, + .depth = 32, + } +}; + +struct viu_dev; +struct viu_buf; + +/* buffer for one video frame */ +struct viu_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct viu_fmt *fmt; +}; + +struct viu_dmaqueue { + struct viu_dev *dev; + struct list_head active; + struct list_head queued; + struct timer_list timeout; +}; + +struct viu_status { + u32 field_irq; + u32 vsync_irq; + u32 hsync_irq; + u32 vstart_irq; + u32 dma_end_irq; + u32 error_irq; +}; + +struct viu_reg { + u32 status_cfg; + u32 luminance; + u32 chroma_r; + u32 chroma_g; + u32 chroma_b; + u32 field_base_addr; + u32 dma_inc; + u32 picture_count; + u32 req_alarm; + u32 alpha; +} __attribute__ ((packed)); + +struct viu_dev { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct mutex lock; + spinlock_t slock; + int users; + + struct device *dev; + /* various device info */ + struct video_device *vdev; + struct viu_dmaqueue vidq; + enum v4l2_field capfield; + int field; + int first; + int dma_done; + + /* Hardware register area */ + struct viu_reg __iomem *vr; + + /* Interrupt vector */ + int irq; + struct viu_status irqs; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct viu_fmt *ovfmt; + unsigned int ovenable; + enum v4l2_field ovfield; + + /* crop */ + struct v4l2_rect crop_current; + + /* clock pointer */ + struct clk *clk; + + /* decoder */ + struct v4l2_subdev *decoder; + + v4l2_std_id std; +}; + +struct viu_fh { + /* must remain the first field of this struct */ + struct v4l2_fh fh; + struct viu_dev *dev; + + /* video capture */ + struct videobuf_queue vb_vidq; + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip clips[1]; + + /* video capture */ + struct viu_fmt *fmt; + int width, height, sizeimage; + enum v4l2_buf_type type; +}; + +static struct viu_reg reg_val; + +/* + * Macro definitions of VIU registers + */ + +/* STATUS_CONFIG register */ +enum status_config { + SOFT_RST = 1 << 0, + + ERR_MASK = 0x0f << 4, /* Error code mask */ + ERR_NO = 0x00, /* No error */ + ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ + ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ + ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ + ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ + ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ + ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ + ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ + ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ + ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ + ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ + + INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ + INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ + INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ + INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ + INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ + INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ + INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ + + INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ + INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ + INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ + INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ + INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ + INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ + + DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ + FIELD_NO = 0x01 << 28, /* Field number */ + DITHER_ON = 0x01 << 29, /* Dithering is on */ + ROUND_ON = 0x01 << 30, /* Round is on */ + MODE_32BIT = 1UL << 31, /* Data in RGBa888, + * 0 in RGB565 + */ +}; + +#define norm_maxw() 720 +#define norm_maxh() 576 + +#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ + INT_HSYNC_STATUS | INT_VSTART_STATUS | \ + INT_DMA_END_STATUS | INT_ERROR_STATUS) + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static irqreturn_t viu_intr(int irq, void *dev_id); + +static struct viu_fmt *format_by_fourcc(int fourcc) +{ + int i; + + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == fourcc) + return formats + i; + } + + dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +static void viu_start_dma(struct viu_dev *dev) +{ + struct viu_reg __iomem *vr = dev->vr; + + dev->field = 0; + + /* Enable DMA operation */ + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(INT_FIELD_EN, &vr->status_cfg); +} + +static void viu_stop_dma(struct viu_dev *dev) +{ + struct viu_reg __iomem *vr = dev->vr; + int cnt = 100; + u32 status_cfg; + + iowrite32be(0, &vr->status_cfg); + + /* Clear pending interrupts */ + status_cfg = ioread32be(&vr->status_cfg); + if (status_cfg & 0x3f0000) + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); + + if (status_cfg & DMA_ACT) { + do { + status_cfg = ioread32be(&vr->status_cfg); + if (status_cfg & INT_DMA_END_STATUS) + break; + } while (cnt--); + + if (cnt < 0) { + /* timed out, issue soft reset */ + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(0, &vr->status_cfg); + } else { + /* clear DMA_END and other pending irqs */ + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); + } + } + + dev->field = 0; +} + +static int restart_video_queue(struct viu_dmaqueue *vidq) +{ + struct viu_buf *buf, *prev; + + dprintk(1, "%s vidq=%p\n", __func__, vidq); + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + dprintk(2, "restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + + viu_stop_dma(vidq->dev); + + /* cancel all outstanding capture requests */ + list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&vidq->queued)) + return 0; + buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); + if (prev == NULL) { + list_move_tail(&buf->vb.queue, &vidq->active); + + dprintk(1, "Restarting video dma\n"); + viu_stop_dma(vidq->dev); + viu_start_dma(vidq->dev); + + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] restart_queue - first active\n", + buf, buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_move_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] restart_queue - move to active\n", + buf, buf->vb.i); + } else { + return 0; + } + prev = buf; + } +} + +static void viu_vid_timeout(struct timer_list *t) +{ + struct viu_dev *dev = from_timer(dev, t, vidq.timeout); + struct viu_buf *buf; + struct viu_dmaqueue *vidq = &dev->vidq; + + while (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); + } + + restart_video_queue(vidq); +} + +/* + * Videobuf operations + */ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct viu_fh *fh = vq->priv_data; + + *size = fh->width * fh->height * fh->fmt->depth >> 3; + if (*count == 0) + *count = 32; + + while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) + (*count)--; + + dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) +{ + struct videobuf_buffer *vb = &buf->vb; + void *vaddr = NULL; + + videobuf_waiton(vq, &buf->vb, 0, 0); + + if (vq->int_ops && vq->int_ops->vaddr) + vaddr = vq->int_ops->vaddr(vb); + + if (vaddr) + videobuf_dma_contig_free(vq, &buf->vb); + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) +{ + struct viu_reg __iomem *vr = dev->vr; + int bpp; + + /* setup the DMA base address */ + reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); + + dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", + buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); + + /* interlace is on by default, set horizontal DMA increment */ + reg_val.status_cfg = 0; + bpp = buf->fmt->depth >> 3; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 4; + break; + default: + dprintk(0, "doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + /* setup picture_count register */ + reg_val.picture_count = (buf->vb.height / 2) << 16 | + buf->vb.width; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + buf->vb.state = VIDEOBUF_ACTIVE; + dev->capfield = buf->vb.field; + + /* reset dma increment if needed */ + if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) + reg_val.dma_inc = 0; + + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); + mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct viu_fh *fh = vq->priv_data; + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + int rc; + + BUG_ON(fh->fmt == NULL); + + if (fh->width < 48 || fh->width > norm_maxw() || + fh->height < 32 || fh->height > norm_maxh()) + return -EINVAL; + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + } + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->fmt = fh->fmt; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = fh->dev; + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_buf *prev; + + if (!list_empty(&vidq->queued)) { + dprintk(1, "adding vb queue=%p\n", &buf->vb.queue); + dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", + vidq, &vidq->queued); + dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", + dev, &vidq->queued, vidq->queued.next, + vidq->queued.prev); + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", + buf, buf->vb.i); + } else if (list_empty(&vidq->active)) { + dprintk(1, "adding vb active=%p\n", &buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] buffer_queue - first active\n", + buf, buf->vb.i); + + buffer_activate(dev, buf); + } else { + dprintk(1, "adding vb queue2=%p\n", &buf->vb.queue); + prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); + if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.i); + } else { + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", + buf, buf->vb.i); + } + } +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + + viu_stop_dma(dev); + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops viu_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* + * IOCTL vidioc handling + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "viu", sizeof(cap->driver)); + strscpy(cap->card, "viu", sizeof(cap->card)); + strscpy(cap->bus_info, "platform:viu", sizeof(cap->bus_info)); + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->pixelformat; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = fh->sizeimage; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fmt *fmt; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + dprintk(1, "Fourcc format (0x%08x) invalid.", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + maxw = norm_maxw(); + maxh = norm_maxh(); + + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + int ret; + + ret = vidioc_try_fmt_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->sizeimage = f->fmt.pix.sizeimage; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + return 0; +} + +static int vidioc_g_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.win = fh->win; + return 0; +} + +static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (dev->ovbuf.base == NULL) + return -EINVAL; + if (dev->ovfmt == NULL) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + + field = win->field; + maxw = dev->crop_current.width; + maxh = dev->crop_current.height; + + if (field == V4L2_FIELD_ANY) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +inline void viu_activate_overlay(struct viu_reg __iomem *vr) +{ + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); +} + +static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) +{ + int bpp; + + dprintk(1, "%s %dx%d\n", __func__, + fh->win.w.width, fh->win.w.height); + + reg_val.status_cfg = 0; + + /* setup window */ + reg_val.picture_count = (fh->win.w.height / 2) << 16 | + fh->win.w.width; + + /* setup color depth and dma increment */ + bpp = dev->ovfmt->depth / 8; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 4; + break; + default: + dprintk(0, "device doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + dev->ovfield = fh->win.field; + if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) + reg_val.dma_inc = 0; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + /* setup the base address of the overlay buffer */ + reg_val.field_base_addr = (u32)(long)dev->ovbuf.base; + + return 0; +} + +static int vidioc_s_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + int err; + + err = verify_preview(dev, &f->fmt.win); + if (err) + return err; + + fh->win = f->fmt.win; + + spin_lock_irqsave(&dev->slock, flags); + viu_setup_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} + +static int vidioc_try_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + return 0; +} + +static int vidioc_overlay(struct file *file, void *priv, unsigned int on) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + + if (on) { + spin_lock_irqsave(&dev->slock, flags); + viu_activate_overlay(dev->vr); + dev->ovenable = 1; + + /* start dma */ + viu_start_dma(dev); + spin_unlock_irqrestore(&dev->slock, flags); + } else { + viu_stop_dma(dev); + dev->ovenable = 0; + } + + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + return 0; +} + +static int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + const struct v4l2_framebuffer *fb = arg; + struct viu_fmt *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (fmt == NULL) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (dev->ovbuf.fmt.bytesperline == 0) { + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width * fmt->depth / 8; + } + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct viu_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + if (dev->ovenable) + dev->ovenable = 0; + + viu_start_dma(fh->dev); + + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + viu_stop_dma(fh->dev); + + return videobuf_streamoff(&fh->vb_vidq); +} + +#define decoder_call(viu, o, f, args...) \ + v4l2_subdev_call(viu->decoder, o, f, ##args) + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct viu_fh *fh = priv; + + decoder_call(fh->dev, video, querystd, std_id); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct viu_fh *fh = priv; + + fh->dev->std = id; + decoder_call(fh->dev, video, s_std, id); + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct viu_fh *fh = priv; + + *std_id = fh->dev->std; + return 0; +} + +/* only one input in this driver */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct viu_fh *fh = priv; + + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = fh->dev->vdev->tvnorms; + strscpy(inp->name, "Camera", sizeof(inp->name)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct viu_fh *fh = priv; + + if (i) + return -EINVAL; + + decoder_call(fh->dev, video, s_routing, i, 0, 0); + return 0; +} + +inline void viu_activate_next_buf(struct viu_dev *dev, + struct viu_dmaqueue *viuq) +{ + struct viu_dmaqueue *vidq = viuq; + struct viu_buf *buf; + + /* launch another DMA operation for an active/queued buffer */ + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, + vb.queue); + dprintk(1, "start another queued buffer: 0x%p\n", buf); + buffer_activate(dev, buf); + } else if (!list_empty(&vidq->queued)) { + buf = list_entry(vidq->queued.next, struct viu_buf, + vb.queue); + list_del(&buf->vb.queue); + + dprintk(1, "start another queued buffer: 0x%p\n", buf); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buffer_activate(dev, buf); + } +} + +inline void viu_default_settings(struct viu_reg __iomem *vr) +{ + iowrite32be(0x9512A254, &vr->luminance); + iowrite32be(0x03310000, &vr->chroma_r); + iowrite32be(0x06600F38, &vr->chroma_g); + iowrite32be(0x00000409, &vr->chroma_b); + iowrite32be(0x000000ff, &vr->alpha); + iowrite32be(0x00000090, &vr->req_alarm); + dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", + ioread32be(&vr->status_cfg), ioread32be(&vr->field_base_addr)); +} + +static void viu_overlay_intr(struct viu_dev *dev, u32 status) +{ + struct viu_reg __iomem *vr = dev->vr; + + if (status & INT_DMA_END_STATUS) + dev->dma_done = 1; + + if (status & INT_FIELD_STATUS) { + if (dev->dma_done) { + u32 addr = reg_val.field_base_addr; + + dev->dma_done = 0; + if (status & FIELD_NO) + addr += reg_val.dma_inc; + + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + } else if (status & INT_VSYNC_STATUS) { + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + } + } +} + +static void viu_capture_intr(struct viu_dev *dev, u32 status) +{ + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_reg __iomem *vr = dev->vr; + struct viu_buf *buf; + int field_num; + int need_two; + int dma_done = 0; + + field_num = status & FIELD_NO; + need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); + + if (status & INT_DMA_END_STATUS) { + dma_done = 1; + if (((field_num == 0) && (dev->field == 0)) || + (field_num && (dev->field == 1))) + dev->field++; + } + + if (status & INT_FIELD_STATUS) { + dprintk(1, "irq: field %d, done %d\n", + !!field_num, dma_done); + if (unlikely(dev->first)) { + if (field_num == 0) { + dev->first = 0; + dprintk(1, "activate first buf\n"); + viu_activate_next_buf(dev, vidq); + } else + dprintk(1, "wait field 0\n"); + return; + } + + /* setup buffer address for next dma operation */ + if (!list_empty(&vidq->active)) { + u32 addr = reg_val.field_base_addr; + + if (field_num && need_two) { + addr += reg_val.dma_inc; + dprintk(1, "field 1, 0x%lx, dev field %d\n", + (unsigned long)addr, dev->field); + } + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + return; + } + } + + if (dma_done && field_num && (dev->field == 2)) { + dev->field = 0; + buf = list_entry(vidq->active.next, + struct viu_buf, vb.queue); + dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", + buf, buf->vb.i, + (unsigned long)videobuf_to_dma_contig(&buf->vb), + (unsigned long)ioread32be(&vr->field_base_addr)); + + if (waitqueue_active(&buf->vb.done)) { + list_del(&buf->vb.queue); + buf->vb.ts = ktime_get_ns(); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + wake_up(&buf->vb.done); + } + /* activate next dma buffer */ + viu_activate_next_buf(dev, vidq); + } +} + +static irqreturn_t viu_intr(int irq, void *dev_id) +{ + struct viu_dev *dev = (struct viu_dev *)dev_id; + struct viu_reg __iomem *vr = dev->vr; + u32 status; + u32 error; + + status = ioread32be(&vr->status_cfg); + + if (status & INT_ERROR_STATUS) { + dev->irqs.error_irq++; + error = status & ERR_MASK; + if (error) + dprintk(1, "Err: error(%d), times:%d!\n", + error >> 4, dev->irqs.error_irq); + /* Clear interrupt error bit and error flags */ + iowrite32be((status & 0xffc0ffff) | INT_ERROR_STATUS, + &vr->status_cfg); + } + + if (status & INT_DMA_END_STATUS) { + dev->irqs.dma_end_irq++; + dev->dma_done = 1; + dprintk(2, "VIU DMA end interrupt times: %d\n", + dev->irqs.dma_end_irq); + } + + if (status & INT_HSYNC_STATUS) + dev->irqs.hsync_irq++; + + if (status & INT_FIELD_STATUS) { + dev->irqs.field_irq++; + dprintk(2, "VIU field interrupt times: %d\n", + dev->irqs.field_irq); + } + + if (status & INT_VSTART_STATUS) + dev->irqs.vstart_irq++; + + if (status & INT_VSYNC_STATUS) { + dev->irqs.vsync_irq++; + dprintk(2, "VIU vsync interrupt times: %d\n", + dev->irqs.vsync_irq); + } + + /* clear all pending irqs */ + status = ioread32be(&vr->status_cfg); + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS), + &vr->status_cfg); + + if (dev->ovenable) { + viu_overlay_intr(dev, status); + return IRQ_HANDLED; + } + + /* Capture mode */ + viu_capture_intr(dev, status); + return IRQ_HANDLED; +} + +/* + * File operations for the device + */ +static int viu_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct viu_dev *dev = video_get_drvdata(vdev); + struct viu_fh *fh; + struct viu_reg __iomem *vr; + int minor = vdev->minor; + u32 status_cfg; + + dprintk(1, "viu: open (minor=%d)\n", minor); + + dev->users++; + if (dev->users > 1) { + dev->users--; + return -EBUSY; + } + + vr = dev->vr; + + dprintk(1, "open minor=%d type=%s users=%d\n", minor, + v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); + + if (mutex_lock_interruptible(&dev->lock)) { + dev->users--; + return -ERESTARTSYS; + } + + /* allocate and initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) { + dev->users--; + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + v4l2_fh_init(&fh->fh, vdev); + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); + fh->width = norm_maxw(); + fh->height = norm_maxh(); + dev->crop_current.width = fh->width; + dev->crop_current.height = fh->height; + + dprintk(1, "Open: fh=%p, dev=%p, dev->vidq=%p\n", fh, dev, &dev->vidq); + dprintk(1, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(1, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + viu_default_settings(vr); + + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | + INT_FIELD_EN | INT_VSTART_EN | + INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN), + &vr->status_cfg); + + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg | INT_ALL_STATUS, &vr->status_cfg); + + spin_lock_init(&fh->vbq_lock); + videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, + dev->dev, &fh->vbq_lock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct viu_buf), fh, + &fh->dev->lock); + v4l2_fh_add(&fh->fh); + mutex_unlock(&dev->lock); + return 0; +} + +static ssize_t viu_read(struct file *file, char __user *data, size_t count, + loff_t *ppos) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret = 0; + + dprintk(2, "%s\n", __func__); + if (dev->ovenable) + dev->ovenable = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + viu_start_dma(dev); + ret = videobuf_read_stream(&fh->vb_vidq, data, count, + ppos, 0, file->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return ret; + } + return 0; +} + +static __poll_t viu_poll(struct file *file, struct poll_table_struct *wait) +{ + struct viu_fh *fh = file->private_data; + struct videobuf_queue *q = &fh->vb_vidq; + struct viu_dev *dev = fh->dev; + __poll_t req_events = poll_requested_events(wait); + __poll_t res = v4l2_ctrl_poll(file, wait); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return EPOLLERR; + + if (!(req_events & (EPOLLIN | EPOLLRDNORM))) + return res; + + mutex_lock(&dev->lock); + res |= videobuf_poll_stream(file, q, wait); + mutex_unlock(&dev->lock); + return res; +} + +static int viu_release(struct file *file) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int minor = video_devdata(file)->minor; + + mutex_lock(&dev->lock); + viu_stop_dma(dev); + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + mutex_unlock(&dev->lock); + + kfree(fh); + + dev->users--; + dprintk(1, "close (minor=%d, users=%d)\n", + minor, dev->users); + return 0; +} + +static void viu_reset(struct viu_reg __iomem *reg) +{ + iowrite32be(0, ®->status_cfg); + iowrite32be(0x9512a254, ®->luminance); + iowrite32be(0x03310000, ®->chroma_r); + iowrite32be(0x06600f38, ®->chroma_g); + iowrite32be(0x00000409, ®->chroma_b); + iowrite32be(0, ®->field_base_addr); + iowrite32be(0, ®->dma_inc); + iowrite32be(0x01e002d0, ®->picture_count); + iowrite32be(0x00000090, ®->req_alarm); + iowrite32be(0x000000ff, ®->alpha); +} + +static int viu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret; + + dprintk(1, "mmap called, vma=%p\n", vma); + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + + dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + ret); + + return ret; +} + +static const struct v4l2_file_operations viu_fops = { + .owner = THIS_MODULE, + .open = viu_open, + .release = viu_release, + .read = viu_read, + .poll = viu_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = viu_mmap, +}; + +static const struct v4l2_ioctl_ops viu_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, + .vidioc_overlay = vidioc_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device viu_template = { + .name = "FSL viu", + .fops = &viu_fops, + .minor = -1, + .ioctl_ops = &viu_ioctl_ops, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE, +}; + +static int viu_of_probe(struct platform_device *op) +{ + struct viu_dev *viu_dev; + struct video_device *vdev; + struct resource r; + struct viu_reg __iomem *viu_regs; + struct i2c_adapter *ad; + int ret, viu_irq; + struct clk *clk; + + ret = of_address_to_resource(op->dev.of_node, 0, &r); + if (ret) { + dev_err(&op->dev, "Can't parse device node resource\n"); + return -ENODEV; + } + + viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (!viu_irq) { + dev_err(&op->dev, "Error while mapping the irq\n"); + return -EINVAL; + } + + /* request mem region */ + if (!devm_request_mem_region(&op->dev, r.start, + sizeof(struct viu_reg), DRV_NAME)) { + dev_err(&op->dev, "Error while requesting mem region\n"); + ret = -EBUSY; + goto err_irq; + } + + /* remap registers */ + viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); + if (!viu_regs) { + dev_err(&op->dev, "Can't map register set\n"); + ret = -ENOMEM; + goto err_irq; + } + + /* Prepare our private structure */ + viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_KERNEL); + if (!viu_dev) { + dev_err(&op->dev, "Can't allocate private structure\n"); + ret = -ENOMEM; + goto err_irq; + } + + viu_dev->vr = viu_regs; + viu_dev->irq = viu_irq; + viu_dev->dev = &op->dev; + + /* init video dma queues */ + INIT_LIST_HEAD(&viu_dev->vidq.active); + INIT_LIST_HEAD(&viu_dev->vidq.queued); + + snprintf(viu_dev->v4l2_dev.name, + sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); + ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); + if (ret < 0) { + dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); + goto err_irq; + } + + ad = i2c_get_adapter(0); + if (!ad) { + ret = -EFAULT; + dev_err(&op->dev, "couldn't get i2c adapter\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&viu_dev->hdl, 5); + if (viu_dev->hdl.error) { + ret = viu_dev->hdl.error; + dev_err(&op->dev, "couldn't register control\n"); + goto err_i2c; + } + /* This control handler will inherit the control(s) from the + sub-device(s). */ + viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl; + viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, + "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); + + timer_setup(&viu_dev->vidq.timeout, viu_vid_timeout, 0); + viu_dev->std = V4L2_STD_NTSC_M; + viu_dev->first = 1; + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto err_hdl; + } + + *vdev = viu_template; + + vdev->v4l2_dev = &viu_dev->v4l2_dev; + + viu_dev->vdev = vdev; + + /* initialize locks */ + mutex_init(&viu_dev->lock); + viu_dev->vdev->lock = &viu_dev->lock; + spin_lock_init(&viu_dev->slock); + + video_set_drvdata(viu_dev->vdev, viu_dev); + + mutex_lock(&viu_dev->lock); + + ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + video_device_release(viu_dev->vdev); + goto err_unlock; + } + + /* enable VIU clock */ + clk = devm_clk_get(&op->dev, "ipg"); + if (IS_ERR(clk)) { + dev_err(&op->dev, "failed to lookup the clock!\n"); + ret = PTR_ERR(clk); + goto err_vdev; + } + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&op->dev, "failed to enable the clock!\n"); + goto err_vdev; + } + viu_dev->clk = clk; + + /* reset VIU module */ + viu_reset(viu_dev->vr); + + /* install interrupt handler */ + if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { + dev_err(&op->dev, "Request VIU IRQ failed.\n"); + ret = -ENODEV; + goto err_clk; + } + + mutex_unlock(&viu_dev->lock); + + dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); + return ret; + +err_clk: + clk_disable_unprepare(viu_dev->clk); +err_vdev: + video_unregister_device(viu_dev->vdev); +err_unlock: + mutex_unlock(&viu_dev->lock); +err_hdl: + v4l2_ctrl_handler_free(&viu_dev->hdl); +err_i2c: + i2c_put_adapter(ad); +err_v4l2: + v4l2_device_unregister(&viu_dev->v4l2_dev); +err_irq: + irq_dispose_mapping(viu_irq); + return ret; +} + +static int viu_of_remove(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sdev); + + free_irq(dev->irq, (void *)dev); + irq_dispose_mapping(dev->irq); + + clk_disable_unprepare(dev->clk); + + v4l2_ctrl_handler_free(&dev->hdl); + video_unregister_device(dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&dev->v4l2_dev); + return 0; +} + +#ifdef CONFIG_PM +static int viu_suspend(struct platform_device *op, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_disable(dev->clk); + return 0; +} + +static int viu_resume(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_enable(dev->clk); + return 0; +} +#endif + +/* + * Initialization and module stuff + */ +static const struct of_device_id mpc512x_viu_of_match[] = { + { + .compatible = "fsl,mpc5121-viu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); + +static struct platform_driver viu_of_platform_driver = { + .probe = viu_of_probe, + .remove = viu_of_remove, +#ifdef CONFIG_PM + .suspend = viu_suspend, + .resume = viu_resume, +#endif + .driver = { + .name = DRV_NAME, + .of_match_table = mpc512x_viu_of_match, + }, +}; + +module_platform_driver(viu_of_platform_driver); + +MODULE_DESCRIPTION("Freescale Video-In(VIU)"); +MODULE_AUTHOR("Hongjun Chen"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VIU_VERSION); -- cgit v1.3-14-g43fede From 6971757bdcccdd420583b0ed52361ebdafed2738 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:47 +0200 Subject: media: davinci: deprecate dm644x_ccdc, dm355_cddc and dm365_isif Deprecate the dm644x_ccdc, dm355_cddc and dm365_isif davinci drivers: all three depend on the vpfe_capture driver, and that driver does not use the vb2 framework for video streaming, instead it uses the old videobuf framework. We want to get rid of these old drivers, so deprecated these for future removal. Note that include/media/davinci/vpfe_capture.h can't be moved to staging since it is used in arch/arm/mach-davinci/davinci.h. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/platform/ti/davinci/Kconfig | 49 - drivers/media/platform/ti/davinci/Makefile | 4 - drivers/media/platform/ti/davinci/ccdc_hw_device.h | 80 - drivers/media/platform/ti/davinci/dm355_ccdc.c | 934 ---------- .../media/platform/ti/davinci/dm355_ccdc_regs.h | 297 --- drivers/media/platform/ti/davinci/dm644x_ccdc.c | 879 --------- .../media/platform/ti/davinci/dm644x_ccdc_regs.h | 140 -- drivers/media/platform/ti/davinci/isif.c | 1127 ------------ drivers/media/platform/ti/davinci/isif_regs.h | 256 --- drivers/media/platform/ti/davinci/vpfe_capture.c | 1902 -------------------- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + .../staging/media/deprecated/vpfe_capture/Kconfig | 58 + .../staging/media/deprecated/vpfe_capture/Makefile | 4 + drivers/staging/media/deprecated/vpfe_capture/TODO | 7 + .../media/deprecated/vpfe_capture/ccdc_hw_device.h | 80 + .../media/deprecated/vpfe_capture/dm355_ccdc.c | 934 ++++++++++ .../media/deprecated/vpfe_capture/dm355_ccdc.h | 308 ++++ .../deprecated/vpfe_capture/dm355_ccdc_regs.h | 297 +++ .../media/deprecated/vpfe_capture/dm644x_ccdc.c | 879 +++++++++ .../media/deprecated/vpfe_capture/dm644x_ccdc.h | 171 ++ .../deprecated/vpfe_capture/dm644x_ccdc_regs.h | 140 ++ .../staging/media/deprecated/vpfe_capture/isif.c | 1127 ++++++++++++ .../staging/media/deprecated/vpfe_capture/isif.h | 518 ++++++ .../media/deprecated/vpfe_capture/isif_regs.h | 256 +++ .../media/deprecated/vpfe_capture/vpfe_capture.c | 1902 ++++++++++++++++++++ include/media/davinci/dm355_ccdc.h | 308 ---- include/media/davinci/dm644x_ccdc.h | 171 -- include/media/davinci/isif.h | 518 ------ 30 files changed, 6684 insertions(+), 6665 deletions(-) delete mode 100644 drivers/media/platform/ti/davinci/ccdc_hw_device.h delete mode 100644 drivers/media/platform/ti/davinci/dm355_ccdc.c delete mode 100644 drivers/media/platform/ti/davinci/dm355_ccdc_regs.h delete mode 100644 drivers/media/platform/ti/davinci/dm644x_ccdc.c delete mode 100644 drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h delete mode 100644 drivers/media/platform/ti/davinci/isif.c delete mode 100644 drivers/media/platform/ti/davinci/isif_regs.h delete mode 100644 drivers/media/platform/ti/davinci/vpfe_capture.c create mode 100644 drivers/staging/media/deprecated/vpfe_capture/Kconfig create mode 100644 drivers/staging/media/deprecated/vpfe_capture/Makefile create mode 100644 drivers/staging/media/deprecated/vpfe_capture/TODO create mode 100644 drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/isif.c create mode 100644 drivers/staging/media/deprecated/vpfe_capture/isif.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/isif_regs.h create mode 100644 drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c delete mode 100644 include/media/davinci/dm355_ccdc.h delete mode 100644 include/media/davinci/dm644x_ccdc.h delete mode 100644 include/media/davinci/isif.h diff --git a/MAINTAINERS b/MAINTAINERS index 2a61d11ed3e5..7f123e4d0f0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20329,6 +20329,7 @@ W: https://linuxtv.org Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git F: drivers/media/platform/ti/davinci/ +F: drivers/staging/media/deprecated/vpfe_capture/ F: include/media/davinci/ TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER diff --git a/drivers/media/platform/ti/davinci/Kconfig b/drivers/media/platform/ti/davinci/Kconfig index c61e697aeb12..96d4bed7fe9e 100644 --- a/drivers/media/platform/ti/davinci/Kconfig +++ b/drivers/media/platform/ti/davinci/Kconfig @@ -32,55 +32,6 @@ config VIDEO_DAVINCI_VPIF_CAPTURE To compile this driver as a module, choose M here. There will be two modules called vpif.ko and vpif_capture.ko -config VIDEO_DM6446_CCDC - tristate "TI DM6446 CCDC video capture driver" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV - depends on ARCH_DAVINCI || COMPILE_TEST - depends on I2C - select VIDEOBUF_DMA_CONTIG - help - Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from slave decoders. - - To compile this driver as a module, choose M here. There will - be three modules called vpfe_capture.ko, vpss.ko and dm644x_ccdc.ko - -config VIDEO_DM355_CCDC - tristate "TI DM355 CCDC video capture driver" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV - depends on ARCH_DAVINCI || COMPILE_TEST - depends on I2C - select VIDEOBUF_DMA_CONTIG - help - Enables DM355 CCD hw module. DM355 CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from a slave decoders - - To compile this driver as a module, choose M here. There will - be three modules called vpfe_capture.ko, vpss.ko and dm355_ccdc.ko - -config VIDEO_DM365_ISIF - tristate "TI DM365 ISIF video capture driver" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV - depends on ARCH_DAVINCI || COMPILE_TEST - depends on I2C - select VIDEOBUF_DMA_CONTIG - help - Enables ISIF hw module. This is the hardware module for - configuring ISIF in VPFE to capture Raw Bayer RGB data from - a image sensor or YUV data from a YUV source. - - To compile this driver as a module, choose M here. There will - be three modules called vpfe_capture.ko, vpss.ko and isif.ko - config VIDEO_DAVINCI_VPBE_DISPLAY tristate "TI DaVinci VPBE V4L2-Display driver" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/ti/davinci/Makefile b/drivers/media/platform/ti/davinci/Makefile index 05c45bf371aa..b20a91653162 100644 --- a/drivers/media/platform/ti/davinci/Makefile +++ b/drivers/media/platform/ti/davinci/Makefile @@ -8,9 +8,5 @@ obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o #VPIF Capture driver obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o -# Capture: DM6446 and DM355 -obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o vpss.o dm644x_ccdc.o -obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o vpss.o dm355_ccdc.o -obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o vpss.o isif.o obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \ vpbe_venc.o vpbe_display.o diff --git a/drivers/media/platform/ti/davinci/ccdc_hw_device.h b/drivers/media/platform/ti/davinci/ccdc_hw_device.h deleted file mode 100644 index a545052a95a9..000000000000 --- a/drivers/media/platform/ti/davinci/ccdc_hw_device.h +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * ccdc device API - */ -#ifndef _CCDC_HW_DEVICE_H -#define _CCDC_HW_DEVICE_H - -#ifdef __KERNEL__ -#include -#include -#include -#include - -/* - * ccdc hw operations - */ -struct ccdc_hw_ops { - /* Pointer to initialize function to initialize ccdc device */ - int (*open) (struct device *dev); - /* Pointer to deinitialize function */ - int (*close) (struct device *dev); - /* set ccdc base address */ - void (*set_ccdc_base)(void *base, int size); - /* Pointer to function to enable or disable ccdc */ - void (*enable) (int en); - /* reset sbl. only for 6446 */ - void (*reset) (void); - /* enable output to sdram */ - void (*enable_out_to_sdram) (int en); - /* Pointer to function to set hw parameters */ - int (*set_hw_if_params) (struct vpfe_hw_if_param *param); - /* get interface parameters */ - int (*get_hw_if_params) (struct vpfe_hw_if_param *param); - /* Pointer to function to configure ccdc */ - int (*configure) (void); - - /* Pointer to function to set buffer type */ - int (*set_buftype) (enum ccdc_buftype buf_type); - /* Pointer to function to get buffer type */ - enum ccdc_buftype (*get_buftype) (void); - /* Pointer to function to set frame format */ - int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); - /* Pointer to function to get frame format */ - enum ccdc_frmfmt (*get_frame_format) (void); - /* enumerate hw pix formats */ - int (*enum_pix)(u32 *hw_pix, int i); - /* Pointer to function to set buffer type */ - u32 (*get_pixel_format) (void); - /* Pointer to function to get pixel format. */ - int (*set_pixel_format) (u32 pixfmt); - /* Pointer to function to set image window */ - int (*set_image_window) (struct v4l2_rect *win); - /* Pointer to function to set image window */ - void (*get_image_window) (struct v4l2_rect *win); - /* Pointer to function to get line length */ - unsigned int (*get_line_length) (void); - - /* Pointer to function to set frame buffer address */ - void (*setfbaddr) (unsigned long addr); - /* Pointer to function to get field id */ - int (*getfid) (void); -}; - -struct ccdc_hw_device { - /* ccdc device name */ - char name[32]; - /* module owner */ - struct module *owner; - /* hw ops */ - struct ccdc_hw_ops hw_ops; -}; - -/* Used by CCDC module to register & unregister with vpfe capture driver */ -int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev); -void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev); - -#endif -#endif diff --git a/drivers/media/platform/ti/davinci/dm355_ccdc.c b/drivers/media/platform/ti/davinci/dm355_ccdc.c deleted file mode 100644 index 8fe55d1b972c..000000000000 --- a/drivers/media/platform/ti/davinci/dm355_ccdc.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2005-2009 Texas Instruments Inc - * - * CCDC hardware module for DM355 - * ------------------------------ - * - * This module is for configuring DM355 CCD controller of VPFE to capture - * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules - * such as Defect Pixel Correction, Color Space Conversion etc to - * pre-process the Bayer RGB data, before writing it to SDRAM. - * - * TODO: 1) Raw bayer parameter settings and bayer capture - * 2) Split module parameter structure to module specific ioctl structs - * 3) add support for lense shading correction - * 4) investigate if enum used for user space type definition - * to be replaced by #defines or integer - */ -#include -#include -#include -#include -#include - -#include -#include - -#include "dm355_ccdc_regs.h" -#include "ccdc_hw_device.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("CCDC Driver for DM355"); -MODULE_AUTHOR("Texas Instruments"); - -static struct ccdc_oper_config { - struct device *dev; - /* CCDC interface type */ - enum vpfe_hw_if_type if_type; - /* Raw Bayer configuration */ - struct ccdc_params_raw bayer; - /* YCbCr configuration */ - struct ccdc_params_ycbcr ycbcr; - /* ccdc base address */ - void __iomem *base_addr; -} ccdc_cfg = { - /* Raw configurations */ - .bayer = { - .pix_fmt = CCDC_PIXFMT_RAW, - .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, - .win = CCDC_WIN_VGA, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .gain = { - .r_ye = 256, - .gb_g = 256, - .gr_cy = 256, - .b_mg = 256 - }, - .config_params = { - .datasft = 2, - .mfilt1 = CCDC_NO_MEDIAN_FILTER1, - .mfilt2 = CCDC_NO_MEDIAN_FILTER2, - .alaw = { - .gamma_wd = 2, - }, - .blk_clamp = { - .sample_pixel = 1, - .dc_sub = 25 - }, - .col_pat_field0 = { - .olop = CCDC_GREEN_BLUE, - .olep = CCDC_BLUE, - .elop = CCDC_RED, - .elep = CCDC_GREEN_RED - }, - .col_pat_field1 = { - .olop = CCDC_GREEN_BLUE, - .olep = CCDC_BLUE, - .elop = CCDC_RED, - .elep = CCDC_GREEN_RED - }, - }, - }, - /* YCbCr configuration */ - .ycbcr = { - .win = CCDC_WIN_PAL, - .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, - .frm_fmt = CCDC_FRMFMT_INTERLACED, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .bt656_enable = 1, - .pix_order = CCDC_PIXORDER_CBYCRY, - .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED - }, -}; - - -/* Raw Bayer formats */ -static u32 ccdc_raw_bayer_pix_formats[] = - {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; - -/* Raw YUV formats */ -static u32 ccdc_raw_yuv_pix_formats[] = - {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; - -/* register access routines */ -static inline u32 regr(u32 offset) -{ - return __raw_readl(ccdc_cfg.base_addr + offset); -} - -static inline void regw(u32 val, u32 offset) -{ - __raw_writel(val, ccdc_cfg.base_addr + offset); -} - -static void ccdc_enable(int en) -{ - unsigned int temp; - temp = regr(SYNCEN); - temp &= (~CCDC_SYNCEN_VDHDEN_MASK); - temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); - regw(temp, SYNCEN); -} - -static void ccdc_enable_output_to_sdram(int en) -{ - unsigned int temp; - temp = regr(SYNCEN); - temp &= (~(CCDC_SYNCEN_WEN_MASK)); - temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); - regw(temp, SYNCEN); -} - -static void ccdc_config_gain_offset(void) -{ - /* configure gain */ - regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); - regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); - regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); - regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); - /* configure offset */ - regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); -} - -/* - * ccdc_restore_defaults() - * This function restore power on defaults in the ccdc registers - */ -static int ccdc_restore_defaults(void) -{ - int i; - - dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); - /* set all registers to zero */ - for (i = 0; i <= CCDC_REG_LAST; i += 4) - regw(0, i); - - /* now override the values with power on defaults in registers */ - regw(MODESET_DEFAULT, MODESET); - /* no culling support */ - regw(CULH_DEFAULT, CULH); - regw(CULV_DEFAULT, CULV); - /* Set default Gain and Offset */ - ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; - ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; - ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; - ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; - ccdc_config_gain_offset(); - regw(OUTCLIP_DEFAULT, OUTCLIP); - regw(LSCCFG2_DEFAULT, LSCCFG2); - /* select ccdc input */ - if (vpss_select_ccdc_source(VPSS_CCDCIN)) { - dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); - return -EFAULT; - } - /* select ccdc clock */ - if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { - dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); - return -EFAULT; - } - dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); - return 0; -} - -static int ccdc_open(struct device *device) -{ - return ccdc_restore_defaults(); -} - -static int ccdc_close(struct device *device) -{ - /* disable clock */ - vpss_enable_clock(VPSS_CCDC_CLOCK, 0); - /* do nothing for now */ - return 0; -} -/* - * ccdc_setwin() - * This function will configure the window size to - * be capture in CCDC reg. - */ -static void ccdc_setwin(struct v4l2_rect *image_win, - enum ccdc_frmfmt frm_fmt, int ppc) -{ - int horz_start, horz_nr_pixels; - int vert_start, vert_nr_lines; - int mid_img = 0; - - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); - - /* - * ppc - per pixel count. indicates how many pixels per cell - * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. - * raw capture this is 1 - */ - horz_start = image_win->left << (ppc - 1); - horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; - - /* Writing the horizontal info into the registers */ - regw(horz_start, SPH); - regw(horz_nr_pixels, NPH); - vert_start = image_win->top; - - if (frm_fmt == CCDC_FRMFMT_INTERLACED) { - vert_nr_lines = (image_win->height >> 1) - 1; - vert_start >>= 1; - /* Since first line doesn't have any data */ - vert_start += 1; - /* configure VDINT0 and VDINT1 */ - regw(vert_start, VDINT0); - } else { - /* Since first line doesn't have any data */ - vert_start += 1; - vert_nr_lines = image_win->height - 1; - /* configure VDINT0 and VDINT1 */ - mid_img = vert_start + (image_win->height / 2); - regw(vert_start, VDINT0); - regw(mid_img, VDINT1); - } - regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); - regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); - regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); - dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); -} - -/* This function will configure CCDC for YCbCr video capture */ -static void ccdc_config_ycbcr(void) -{ - struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; - u32 temp; - - /* first set the CCDC power on defaults values in all registers */ - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); - ccdc_restore_defaults(); - - /* configure pixel format & video frame format */ - temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << - CCDC_INPUT_MODE_SHIFT) | - ((params->frm_fmt & CCDC_FRM_FMT_MASK) << - CCDC_FRM_FMT_SHIFT)); - - /* setup BT.656 sync mode */ - if (params->bt656_enable) { - regw(CCDC_REC656IF_BT656_EN, REC656IF); - /* - * configure the FID, VD, HD pin polarity fld,hd pol positive, - * vd negative, 8-bit pack mode - */ - temp |= CCDC_VD_POL_NEGATIVE; - } else { /* y/c external sync mode */ - temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << - CCDC_FID_POL_SHIFT) | - ((params->hd_pol & CCDC_HD_POL_MASK) << - CCDC_HD_POL_SHIFT) | - ((params->vd_pol & CCDC_VD_POL_MASK) << - CCDC_VD_POL_SHIFT)); - } - - /* pack the data to 8-bit */ - temp |= CCDC_DATA_PACK_ENABLE; - - regw(temp, MODESET); - - /* configure video window */ - ccdc_setwin(¶ms->win, params->frm_fmt, 2); - - /* configure the order of y cb cr in SD-RAM */ - temp = (params->pix_order << CCDC_Y8POS_SHIFT); - temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; - regw(temp, CCDCFG); - - /* - * configure the horizontal line offset. This is done by rounding up - * width to a multiple of 16 pixels and multiply by two to account for - * y:cb:cr 4:2:2 data - */ - regw(((params->win.width * 2 + 31) >> 5), HSIZE); - - /* configure the memory line offset */ - if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { - /* two fields are interleaved in memory */ - regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); - } - - dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); -} - -/* - * ccdc_config_black_clamp() - * configure parameters for Optical Black Clamp - */ -static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) -{ - u32 val; - - if (!bclamp->b_clamp_enable) { - /* configure DCSub */ - regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); - regw(0x0000, CLAMP); - return; - } - /* Enable the Black clamping, set sample lines and pixels */ - val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | - ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << - CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; - regw(val, CLAMP); - - /* If Black clamping is enable then make dcsub 0 */ - val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) - << CCDC_NUM_LINE_CALC_SHIFT; - regw(val, DCSUB); -} - -/* - * ccdc_config_black_compense() - * configure parameters for Black Compensation - */ -static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) -{ - u32 val; - - val = (bcomp->b & CCDC_BLK_COMP_MASK) | - ((bcomp->gb & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_GB_COMP_SHIFT); - regw(val, BLKCMP1); - - val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_GR_COMP_SHIFT) | - ((bcomp->r & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_R_COMP_SHIFT); - regw(val, BLKCMP0); -} - -/* - * ccdc_write_dfc_entry() - * write an entry in the dfc table. - */ -static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) -{ -/* TODO This is to be re-visited and adjusted */ -#define DFC_WRITE_WAIT_COUNT 1000 - u32 val, count = DFC_WRITE_WAIT_COUNT; - - regw(dfc->dft_corr_vert[index], DFCMEM0); - regw(dfc->dft_corr_horz[index], DFCMEM1); - regw(dfc->dft_corr_sub1[index], DFCMEM2); - regw(dfc->dft_corr_sub2[index], DFCMEM3); - regw(dfc->dft_corr_sub3[index], DFCMEM4); - /* set WR bit to write */ - val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; - regw(val, DFCMEMCTL); - - /* - * Assume, it is very short. If we get an error, we need to - * adjust this value - */ - while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) - count--; - /* - * TODO We expect the count to be non-zero to be successful. Adjust - * the count if write requires more time - */ - - if (count) { - dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); - return -1; - } - return 0; -} - -/* - * ccdc_config_vdfc() - * configure parameters for Vertical Defect Correction - */ -static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) -{ - u32 val; - int i; - - /* Configure General Defect Correction. The table used is from IPIPE */ - val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; - - /* Configure Vertical Defect Correction if needed */ - if (!dfc->ver_dft_en) { - /* Enable only General Defect Correction */ - regw(val, DFCCTL); - return 0; - } - - if (dfc->table_size > CCDC_DFT_TABLE_SIZE) - return -EINVAL; - - val |= CCDC_DFCCTL_VDFC_DISABLE; - val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << - CCDC_DFCCTL_VDFCSL_SHIFT; - val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << - CCDC_DFCCTL_VDFCUDA_SHIFT; - val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << - CCDC_DFCCTL_VDFLSFT_SHIFT; - regw(val , DFCCTL); - - /* clear address ptr to offset 0 */ - val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; - - /* write defect table entries */ - for (i = 0; i < dfc->table_size; i++) { - /* increment address for non zero index */ - if (i != 0) - val = CCDC_DFCMEMCTL_INC_ADDR; - regw(val, DFCMEMCTL); - if (ccdc_write_dfc_entry(i, dfc) < 0) - return -EFAULT; - } - - /* update saturation level and enable dfc */ - regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); - val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << - CCDC_DFCCTL_VDFCEN_SHIFT); - regw(val, DFCCTL); - return 0; -} - -/* - * ccdc_config_csc() - * configure parameters for color space conversion - * Each register CSCM0-7 has two values in S8Q5 format. - */ -static void ccdc_config_csc(struct ccdc_csc *csc) -{ - u32 val1 = 0, val2; - int i; - - if (!csc->enable) - return; - - /* Enable the CSC sub-module */ - regw(CCDC_CSC_ENABLE, CSCCTL); - - /* Converting the co-eff as per the format of the register */ - for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { - if ((i % 2) == 0) { - /* CSCM - LSB */ - val1 = (csc->coeff[i].integer & - CCDC_CSC_COEF_INTEG_MASK) - << CCDC_CSC_COEF_INTEG_SHIFT; - /* - * convert decimal part to binary. Use 2 decimal - * precision, user values range from .00 - 0.99 - */ - val1 |= (((csc->coeff[i].decimal & - CCDC_CSC_COEF_DECIMAL_MASK) * - CCDC_CSC_DEC_MAX) / 100); - } else { - - /* CSCM - MSB */ - val2 = (csc->coeff[i].integer & - CCDC_CSC_COEF_INTEG_MASK) - << CCDC_CSC_COEF_INTEG_SHIFT; - val2 |= (((csc->coeff[i].decimal & - CCDC_CSC_COEF_DECIMAL_MASK) * - CCDC_CSC_DEC_MAX) / 100); - val2 <<= CCDC_CSCM_MSB_SHIFT; - val2 |= val1; - regw(val2, (CSCM0 + ((i - 1) << 1))); - } - } -} - -/* - * ccdc_config_color_patterns() - * configure parameters for color patterns - */ -static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, - struct ccdc_col_pat *pat1) -{ - u32 val; - - val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | - (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | - (pat1->elop << 12) | (pat1->elep << 14)); - regw(val, COLPTN); -} - -/* This function will configure CCDC for Raw mode image capture */ -static int ccdc_config_raw(void) -{ - struct ccdc_params_raw *params = &ccdc_cfg.bayer; - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int val; - - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); - - /* restore power on defaults to register */ - ccdc_restore_defaults(); - - /* CCDCFG register: - * set CCD Not to swap input since input is RAW data - * set FID detection function to Latch at V-Sync - * set WENLOG - ccdc valid area to AND - * set TRGSEL to WENBIT - * set EXTRG to DISABLE - * disable latching function on VSYNC - shadowed registers - */ - regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | - CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | - CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); - - /* - * Set VDHD direction to input, input type to raw input - * normal data polarity, do not use external WEN - */ - val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | - CCDC_EXWEN_DISABLE); - - /* - * Configure the vertical sync polarity (MODESET.VDPOL), horizontal - * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), - * frame format(progressive or interlace), & pixel format (Input mode) - */ - val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | - ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | - ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | - ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | - ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); - - /* set pack for alaw compression */ - if ((config_params->data_sz == CCDC_DATA_8BITS) || - config_params->alaw.enable) - val |= CCDC_DATA_PACK_ENABLE; - - /* Configure for LPF */ - if (config_params->lpf_enable) - val |= (config_params->lpf_enable & CCDC_LPF_MASK) << - CCDC_LPF_SHIFT; - - /* Configure the data shift */ - val |= (config_params->datasft & CCDC_DATASFT_MASK) << - CCDC_DATASFT_SHIFT; - regw(val , MODESET); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); - - /* Configure the Median Filter threshold */ - regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); - - /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ - val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | - CCDC_CFA_MOSAIC; - - /* Enable and configure aLaw register if needed */ - if (config_params->alaw.enable) { - val |= (CCDC_ALAW_ENABLE | - ((config_params->alaw.gamma_wd & - CCDC_ALAW_GAMMA_WD_MASK) << - CCDC_GAMMAWD_INPUT_SHIFT)); - } - - /* Configure Median filter1 & filter2 */ - val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | - (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); - - regw(val, GAMMAWD); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); - - /* configure video window */ - ccdc_setwin(¶ms->win, params->frm_fmt, 1); - - /* Optical Clamp Averaging */ - ccdc_config_black_clamp(&config_params->blk_clamp); - - /* Black level compensation */ - ccdc_config_black_compense(&config_params->blk_comp); - - /* Vertical Defect Correction if needed */ - if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) - return -EFAULT; - - /* color space conversion */ - ccdc_config_csc(&config_params->csc); - - /* color pattern */ - ccdc_config_color_patterns(&config_params->col_pat_field0, - &config_params->col_pat_field1); - - /* Configure the Gain & offset control */ - ccdc_config_gain_offset(); - - dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); - - /* Configure DATAOFST register */ - val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << - CCDC_DATAOFST_H_SHIFT; - val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << - CCDC_DATAOFST_V_SHIFT; - regw(val, DATAOFST); - - /* configuring HSIZE register */ - val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << - CCDC_HSIZE_FLIP_SHIFT; - - /* If pack 8 is enable then 1 pixel will take 1 byte */ - if ((config_params->data_sz == CCDC_DATA_8BITS) || - config_params->alaw.enable) { - val |= (((params->win.width) + 31) >> 5) & - CCDC_HSIZE_VAL_MASK; - - /* adjust to multiple of 32 */ - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", - (((params->win.width) + 31) >> 5) & - CCDC_HSIZE_VAL_MASK); - } else { - /* else one pixel will take 2 byte */ - val |= (((params->win.width * 2) + 31) >> 5) & - CCDC_HSIZE_VAL_MASK; - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", - (((params->win.width * 2) + 31) >> 5) & - CCDC_HSIZE_VAL_MASK); - } - regw(val, HSIZE); - - /* Configure SDOFST register */ - if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { - if (params->image_invert_enable) { - /* For interlace inverse mode */ - regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", - CCDC_SDOFST_INTERLACE_INVERSE); - } else { - /* For interlace non inverse mode */ - regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", - CCDC_SDOFST_INTERLACE_NORMAL); - } - } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { - if (params->image_invert_enable) { - /* For progessive inverse mode */ - regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", - CCDC_SDOFST_PROGRESSIVE_INVERSE); - } else { - /* For progessive non inverse mode */ - regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", - CCDC_SDOFST_PROGRESSIVE_NORMAL); - } - } - dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); - return 0; -} - -static int ccdc_configure(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - return ccdc_config_raw(); - else - ccdc_config_ycbcr(); - return 0; -} - -static int ccdc_set_buftype(enum ccdc_buftype buf_type) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.buf_type = buf_type; - else - ccdc_cfg.ycbcr.buf_type = buf_type; - return 0; -} -static enum ccdc_buftype ccdc_get_buftype(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - return ccdc_cfg.bayer.buf_type; - return ccdc_cfg.ycbcr.buf_type; -} - -static int ccdc_enum_pix(u32 *pix, int i) -{ - int ret = -EINVAL; - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { - *pix = ccdc_raw_bayer_pix_formats[i]; - ret = 0; - } - } else { - if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { - *pix = ccdc_raw_yuv_pix_formats[i]; - ret = 0; - } - } - return ret; -} - -static int ccdc_set_pixel_format(u32 pixfmt) -{ - struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; - - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; - if (pixfmt == V4L2_PIX_FMT_SBGGR8) - alaw->enable = 1; - else if (pixfmt != V4L2_PIX_FMT_SBGGR16) - return -EINVAL; - } else { - if (pixfmt == V4L2_PIX_FMT_YUYV) - ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; - else if (pixfmt == V4L2_PIX_FMT_UYVY) - ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; - else - return -EINVAL; - } - return 0; -} -static u32 ccdc_get_pixel_format(void) -{ - struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; - u32 pixfmt; - - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - if (alaw->enable) - pixfmt = V4L2_PIX_FMT_SBGGR8; - else - pixfmt = V4L2_PIX_FMT_SBGGR16; - else { - if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) - pixfmt = V4L2_PIX_FMT_YUYV; - else - pixfmt = V4L2_PIX_FMT_UYVY; - } - return pixfmt; -} -static int ccdc_set_image_window(struct v4l2_rect *win) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.win = *win; - else - ccdc_cfg.ycbcr.win = *win; - return 0; -} - -static void ccdc_get_image_window(struct v4l2_rect *win) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - *win = ccdc_cfg.bayer.win; - else - *win = ccdc_cfg.ycbcr.win; -} - -static unsigned int ccdc_get_line_length(void) -{ - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int len; - - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - if ((config_params->alaw.enable) || - (config_params->data_sz == CCDC_DATA_8BITS)) - len = ccdc_cfg.bayer.win.width; - else - len = ccdc_cfg.bayer.win.width * 2; - } else - len = ccdc_cfg.ycbcr.win.width * 2; - return ALIGN(len, 32); -} - -static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.frm_fmt = frm_fmt; - else - ccdc_cfg.ycbcr.frm_fmt = frm_fmt; - return 0; -} - -static enum ccdc_frmfmt ccdc_get_frame_format(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - return ccdc_cfg.bayer.frm_fmt; - else - return ccdc_cfg.ycbcr.frm_fmt; -} - -static int ccdc_getfid(void) -{ - return (regr(MODESET) >> 15) & 1; -} - -/* misc operations */ -static inline void ccdc_setfbaddr(unsigned long addr) -{ - regw((addr >> 21) & 0x007f, STADRH); - regw((addr >> 5) & 0x0ffff, STADRL); -} - -static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) -{ - ccdc_cfg.if_type = params->if_type; - - switch (params->if_type) { - case VPFE_BT656: - case VPFE_YCBCR_SYNC_16: - case VPFE_YCBCR_SYNC_8: - ccdc_cfg.ycbcr.vd_pol = params->vdpol; - ccdc_cfg.ycbcr.hd_pol = params->hdpol; - break; - default: - /* TODO add support for raw bayer here */ - return -EINVAL; - } - return 0; -} - -static const struct ccdc_hw_device ccdc_hw_dev = { - .name = "DM355 CCDC", - .owner = THIS_MODULE, - .hw_ops = { - .open = ccdc_open, - .close = ccdc_close, - .enable = ccdc_enable, - .enable_out_to_sdram = ccdc_enable_output_to_sdram, - .set_hw_if_params = ccdc_set_hw_if_params, - .configure = ccdc_configure, - .set_buftype = ccdc_set_buftype, - .get_buftype = ccdc_get_buftype, - .enum_pix = ccdc_enum_pix, - .set_pixel_format = ccdc_set_pixel_format, - .get_pixel_format = ccdc_get_pixel_format, - .set_frame_format = ccdc_set_frame_format, - .get_frame_format = ccdc_get_frame_format, - .set_image_window = ccdc_set_image_window, - .get_image_window = ccdc_get_image_window, - .get_line_length = ccdc_get_line_length, - .setfbaddr = ccdc_setfbaddr, - .getfid = ccdc_getfid, - }, -}; - -static int dm355_ccdc_probe(struct platform_device *pdev) -{ - void (*setup_pinmux)(void); - struct resource *res; - int status = 0; - - /* - * first try to register with vpfe. If not correct platform, then we - * don't have to iomap - */ - status = vpfe_register_ccdc_device(&ccdc_hw_dev); - if (status < 0) - return status; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - status = -ENODEV; - goto fail_nores; - } - - res = request_mem_region(res->start, resource_size(res), res->name); - if (!res) { - status = -EBUSY; - goto fail_nores; - } - - ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); - if (!ccdc_cfg.base_addr) { - status = -ENOMEM; - goto fail_nomem; - } - - /* Platform data holds setup_pinmux function ptr */ - if (NULL == pdev->dev.platform_data) { - status = -ENODEV; - goto fail_nomap; - } - setup_pinmux = pdev->dev.platform_data; - /* - * setup Mux configuration for ccdc which may be different for - * different SoCs using this CCDC - */ - setup_pinmux(); - ccdc_cfg.dev = &pdev->dev; - printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); - return 0; -fail_nomap: - iounmap(ccdc_cfg.base_addr); -fail_nomem: - release_mem_region(res->start, resource_size(res)); -fail_nores: - vpfe_unregister_ccdc_device(&ccdc_hw_dev); - return status; -} - -static int dm355_ccdc_remove(struct platform_device *pdev) -{ - struct resource *res; - - iounmap(ccdc_cfg.base_addr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - vpfe_unregister_ccdc_device(&ccdc_hw_dev); - return 0; -} - -static struct platform_driver dm355_ccdc_driver = { - .driver = { - .name = "dm355_ccdc", - }, - .remove = dm355_ccdc_remove, - .probe = dm355_ccdc_probe, -}; - -module_platform_driver(dm355_ccdc_driver); diff --git a/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h deleted file mode 100644 index eb381f075245..000000000000 --- a/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h +++ /dev/null @@ -1,297 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005-2009 Texas Instruments Inc - */ -#ifndef _DM355_CCDC_REGS_H -#define _DM355_CCDC_REGS_H - -/**************************************************************************\ -* Register OFFSET Definitions -\**************************************************************************/ -#define SYNCEN 0x00 -#define MODESET 0x04 -#define HDWIDTH 0x08 -#define VDWIDTH 0x0c -#define PPLN 0x10 -#define LPFR 0x14 -#define SPH 0x18 -#define NPH 0x1c -#define SLV0 0x20 -#define SLV1 0x24 -#define NLV 0x28 -#define CULH 0x2c -#define CULV 0x30 -#define HSIZE 0x34 -#define SDOFST 0x38 -#define STADRH 0x3c -#define STADRL 0x40 -#define CLAMP 0x44 -#define DCSUB 0x48 -#define COLPTN 0x4c -#define BLKCMP0 0x50 -#define BLKCMP1 0x54 -#define MEDFILT 0x58 -#define RYEGAIN 0x5c -#define GRCYGAIN 0x60 -#define GBGGAIN 0x64 -#define BMGGAIN 0x68 -#define OFFSET 0x6c -#define OUTCLIP 0x70 -#define VDINT0 0x74 -#define VDINT1 0x78 -#define RSV0 0x7c -#define GAMMAWD 0x80 -#define REC656IF 0x84 -#define CCDCFG 0x88 -#define FMTCFG 0x8c -#define FMTPLEN 0x90 -#define FMTSPH 0x94 -#define FMTLNH 0x98 -#define FMTSLV 0x9c -#define FMTLNV 0xa0 -#define FMTRLEN 0xa4 -#define FMTHCNT 0xa8 -#define FMT_ADDR_PTR_B 0xac -#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) -#define FMTPGM_VF0 0xcc -#define FMTPGM_VF1 0xd0 -#define FMTPGM_AP0 0xd4 -#define FMTPGM_AP1 0xd8 -#define FMTPGM_AP2 0xdc -#define FMTPGM_AP3 0xe0 -#define FMTPGM_AP4 0xe4 -#define FMTPGM_AP5 0xe8 -#define FMTPGM_AP6 0xec -#define FMTPGM_AP7 0xf0 -#define LSCCFG1 0xf4 -#define LSCCFG2 0xf8 -#define LSCH0 0xfc -#define LSCV0 0x100 -#define LSCKH 0x104 -#define LSCKV 0x108 -#define LSCMEMCTL 0x10c -#define LSCMEMD 0x110 -#define LSCMEMQ 0x114 -#define DFCCTL 0x118 -#define DFCVSAT 0x11c -#define DFCMEMCTL 0x120 -#define DFCMEM0 0x124 -#define DFCMEM1 0x128 -#define DFCMEM2 0x12c -#define DFCMEM3 0x130 -#define DFCMEM4 0x134 -#define CSCCTL 0x138 -#define CSCM0 0x13c -#define CSCM1 0x140 -#define CSCM2 0x144 -#define CSCM3 0x148 -#define CSCM4 0x14c -#define CSCM5 0x150 -#define CSCM6 0x154 -#define CSCM7 0x158 -#define DATAOFST 0x15c -#define CCDC_REG_LAST DATAOFST -/************************************************************** -* Define for various register bit mask and shifts for CCDC -* -**************************************************************/ -#define CCDC_RAW_IP_MODE 0 -#define CCDC_VDHDOUT_INPUT 0 -#define CCDC_YCINSWP_RAW (0 << 4) -#define CCDC_EXWEN_DISABLE 0 -#define CCDC_DATAPOL_NORMAL 0 -#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 -#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) -#define CCDC_CCDCFG_WENLOG_AND 0 -#define CCDC_CCDCFG_TRGSEL_WEN 0 -#define CCDC_CCDCFG_EXTRG_DISABLE 0 -#define CCDC_CFA_MOSAIC 0 -#define CCDC_Y8POS_SHIFT 11 - -#define CCDC_VDC_DFCVSAT_MASK 0x3fff -#define CCDC_DATAOFST_MASK 0x0ff -#define CCDC_DATAOFST_H_SHIFT 0 -#define CCDC_DATAOFST_V_SHIFT 8 -#define CCDC_GAMMAWD_CFA_MASK 1 -#define CCDC_GAMMAWD_CFA_SHIFT 5 -#define CCDC_GAMMAWD_INPUT_SHIFT 2 -#define CCDC_FID_POL_MASK 1 -#define CCDC_FID_POL_SHIFT 4 -#define CCDC_HD_POL_MASK 1 -#define CCDC_HD_POL_SHIFT 3 -#define CCDC_VD_POL_MASK 1 -#define CCDC_VD_POL_SHIFT 2 -#define CCDC_VD_POL_NEGATIVE (1 << 2) -#define CCDC_FRM_FMT_MASK 1 -#define CCDC_FRM_FMT_SHIFT 7 -#define CCDC_DATA_SZ_MASK 7 -#define CCDC_DATA_SZ_SHIFT 8 -#define CCDC_VDHDOUT_MASK 1 -#define CCDC_VDHDOUT_SHIFT 0 -#define CCDC_EXWEN_MASK 1 -#define CCDC_EXWEN_SHIFT 5 -#define CCDC_INPUT_MODE_MASK 3 -#define CCDC_INPUT_MODE_SHIFT 12 -#define CCDC_PIX_FMT_MASK 3 -#define CCDC_PIX_FMT_SHIFT 12 -#define CCDC_DATAPOL_MASK 1 -#define CCDC_DATAPOL_SHIFT 6 -#define CCDC_WEN_ENABLE (1 << 1) -#define CCDC_VDHDEN_ENABLE (1 << 16) -#define CCDC_LPF_ENABLE (1 << 14) -#define CCDC_ALAW_ENABLE 1 -#define CCDC_ALAW_GAMMA_WD_MASK 7 -#define CCDC_REC656IF_BT656_EN 3 - -#define CCDC_FMTCFG_FMTMODE_MASK 3 -#define CCDC_FMTCFG_FMTMODE_SHIFT 1 -#define CCDC_FMTCFG_LNUM_MASK 3 -#define CCDC_FMTCFG_LNUM_SHIFT 4 -#define CCDC_FMTCFG_ADDRINC_MASK 7 -#define CCDC_FMTCFG_ADDRINC_SHIFT 8 - -#define CCDC_CCDCFG_FIDMD_SHIFT 6 -#define CCDC_CCDCFG_WENLOG_SHIFT 8 -#define CCDC_CCDCFG_TRGSEL_SHIFT 9 -#define CCDC_CCDCFG_EXTRG_SHIFT 10 -#define CCDC_CCDCFG_MSBINVI_SHIFT 13 - -#define CCDC_HSIZE_FLIP_SHIFT 12 -#define CCDC_HSIZE_FLIP_MASK 1 -#define CCDC_HSIZE_VAL_MASK 0xFFF -#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 -#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D -#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D -#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 -#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 -#define CCDC_START_PX_HOR_MASK 0x7FFF -#define CCDC_NUM_PX_HOR_MASK 0x7FFF -#define CCDC_START_VER_ONE_MASK 0x7FFF -#define CCDC_START_VER_TWO_MASK 0x7FFF -#define CCDC_NUM_LINES_VER 0x7FFF - -#define CCDC_BLK_CLAMP_ENABLE (1 << 15) -#define CCDC_BLK_SGAIN_MASK 0x1F -#define CCDC_BLK_ST_PXL_MASK 0x1FFF -#define CCDC_BLK_SAMPLE_LN_MASK 3 -#define CCDC_BLK_SAMPLE_LN_SHIFT 13 - -#define CCDC_NUM_LINE_CALC_MASK 3 -#define CCDC_NUM_LINE_CALC_SHIFT 14 - -#define CCDC_BLK_DC_SUB_MASK 0x3FFF -#define CCDC_BLK_COMP_MASK 0xFF -#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 -#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 -#define CCDC_BLK_COMP_R_COMP_SHIFT 8 -#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) -#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) -#define CCDC_FPC_ENABLE (1 << 15) -#define CCDC_FPC_FPC_NUM_MASK 0x7FFF -#define CCDC_DATA_PACK_ENABLE (1 << 11) -#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF -#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF -#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 -#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF -#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF -#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 -#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF -#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 -#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF -#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 -#define CCDC_VP_OUT_HORZ_ST_MASK 0xF - -#define CCDC_CSC_COEF_INTEG_MASK 7 -#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f -#define CCDC_CSC_COEF_INTEG_SHIFT 5 -#define CCDC_CSCM_MSB_SHIFT 8 -#define CCDC_CSC_ENABLE 1 -#define CCDC_CSC_DEC_MAX 32 - -#define CCDC_MFILT1_SHIFT 10 -#define CCDC_MFILT2_SHIFT 8 -#define CCDC_MED_FILT_THRESH 0x3FFF -#define CCDC_LPF_MASK 1 -#define CCDC_LPF_SHIFT 14 -#define CCDC_OFFSET_MASK 0x3FF -#define CCDC_DATASFT_MASK 7 -#define CCDC_DATASFT_SHIFT 8 - -#define CCDC_DF_ENABLE 1 - -#define CCDC_FMTPLEN_P0_MASK 0xF -#define CCDC_FMTPLEN_P1_MASK 0xF -#define CCDC_FMTPLEN_P2_MASK 7 -#define CCDC_FMTPLEN_P3_MASK 7 -#define CCDC_FMTPLEN_P0_SHIFT 0 -#define CCDC_FMTPLEN_P1_SHIFT 4 -#define CCDC_FMTPLEN_P2_SHIFT 8 -#define CCDC_FMTPLEN_P3_SHIFT 12 - -#define CCDC_FMTSPH_MASK 0x1FFF -#define CCDC_FMTLNH_MASK 0x1FFF -#define CCDC_FMTSLV_MASK 0x1FFF -#define CCDC_FMTLNV_MASK 0x7FFF -#define CCDC_FMTRLEN_MASK 0x1FFF -#define CCDC_FMTHCNT_MASK 0x1FFF - -#define CCDC_ADP_INIT_MASK 0x1FFF -#define CCDC_ADP_LINE_SHIFT 13 -#define CCDC_ADP_LINE_MASK 3 -#define CCDC_FMTPGN_APTR_MASK 7 - -#define CCDC_DFCCTL_GDFCEN_MASK 1 -#define CCDC_DFCCTL_VDFCEN_MASK 1 -#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) -#define CCDC_DFCCTL_VDFCEN_SHIFT 4 -#define CCDC_DFCCTL_VDFCSL_MASK 3 -#define CCDC_DFCCTL_VDFCSL_SHIFT 5 -#define CCDC_DFCCTL_VDFCUDA_MASK 1 -#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 -#define CCDC_DFCCTL_VDFLSFT_MASK 3 -#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 -#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 -#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 -#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 -#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 -#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) - -#define CCDC_LSCCFG_GFTSF_MASK 7 -#define CCDC_LSCCFG_GFTSF_SHIFT 1 -#define CCDC_LSCCFG_GFTINV_MASK 0xf -#define CCDC_LSCCFG_GFTINV_SHIFT 4 -#define CCDC_LSC_GFTABLE_SEL_MASK 3 -#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 -#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 -#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 -#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 -#define CCDC_LSC_GFMODE_MASK 3 -#define CCDC_LSC_GFMODE_SHIFT 4 -#define CCDC_LSC_DISABLE 0 -#define CCDC_LSC_ENABLE 1 -#define CCDC_LSC_TABLE1_SLC 0 -#define CCDC_LSC_TABLE2_SLC 1 -#define CCDC_LSC_TABLE3_SLC 2 -#define CCDC_LSC_MEMADDR_RESET (1 << 2) -#define CCDC_LSC_MEMADDR_INCR (0 << 2) -#define CCDC_LSC_FRAC_MASK_T1 0xFF -#define CCDC_LSC_INT_MASK 3 -#define CCDC_LSC_FRAC_MASK 0x3FFF -#define CCDC_LSC_CENTRE_MASK 0x3FFF -#define CCDC_LSC_COEF_MASK 0xff -#define CCDC_LSC_COEFL_SHIFT 0 -#define CCDC_LSC_COEFU_SHIFT 8 -#define CCDC_GAIN_MASK 0x7FF -#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) -#define CCDC_SYNCEN_WEN_MASK (1 << 1) -#define CCDC_SYNCEN_WEN_SHIFT 1 - -/* Power on Defaults in hardware */ -#define MODESET_DEFAULT 0x200 -#define CULH_DEFAULT 0xFFFF -#define CULV_DEFAULT 0xFF -#define GAIN_DEFAULT 256 -#define OUTCLIP_DEFAULT 0x3FFF -#define LSCCFG2_DEFAULT 0xE - -#endif diff --git a/drivers/media/platform/ti/davinci/dm644x_ccdc.c b/drivers/media/platform/ti/davinci/dm644x_ccdc.c deleted file mode 100644 index e4073e99914c..000000000000 --- a/drivers/media/platform/ti/davinci/dm644x_ccdc.c +++ /dev/null @@ -1,879 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2006-2009 Texas Instruments Inc - * - * CCDC hardware module for DM6446 - * ------------------------------ - * - * This module is for configuring CCD controller of DM6446 VPFE to capture - * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules - * such as Defect Pixel Correction, Color Space Conversion etc to - * pre-process the Raw Bayer RGB data, before writing it to SDRAM. - * This file is named DM644x so that other variants such DM6443 - * may be supported using the same module. - * - * TODO: Test Raw bayer parameter settings and bayer capture - * Split module parameter structure to module specific ioctl structs - * investigate if enum used for user space type definition - * to be replaced by #defines or integer - */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "dm644x_ccdc_regs.h" -#include "ccdc_hw_device.h" - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("CCDC Driver for DM6446"); -MODULE_AUTHOR("Texas Instruments"); - -static struct ccdc_oper_config { - struct device *dev; - /* CCDC interface type */ - enum vpfe_hw_if_type if_type; - /* Raw Bayer configuration */ - struct ccdc_params_raw bayer; - /* YCbCr configuration */ - struct ccdc_params_ycbcr ycbcr; - /* ccdc base address */ - void __iomem *base_addr; -} ccdc_cfg = { - /* Raw configurations */ - .bayer = { - .pix_fmt = CCDC_PIXFMT_RAW, - .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, - .win = CCDC_WIN_VGA, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .config_params = { - .data_sz = CCDC_DATA_10BITS, - }, - }, - .ycbcr = { - .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, - .frm_fmt = CCDC_FRMFMT_INTERLACED, - .win = CCDC_WIN_PAL, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .bt656_enable = 1, - .pix_order = CCDC_PIXORDER_CBYCRY, - .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED - }, -}; - -#define CCDC_MAX_RAW_YUV_FORMATS 2 - -/* Raw Bayer formats */ -static u32 ccdc_raw_bayer_pix_formats[] = - {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; - -/* Raw YUV formats */ -static u32 ccdc_raw_yuv_pix_formats[] = - {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; - -/* CCDC Save/Restore context */ -static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; - -/* register access routines */ -static inline u32 regr(u32 offset) -{ - return __raw_readl(ccdc_cfg.base_addr + offset); -} - -static inline void regw(u32 val, u32 offset) -{ - __raw_writel(val, ccdc_cfg.base_addr + offset); -} - -static void ccdc_enable(int flag) -{ - regw(flag, CCDC_PCR); -} - -static void ccdc_enable_vport(int flag) -{ - if (flag) - /* enable video port */ - regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); - else - regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); -} - -/* - * ccdc_setwin() - * This function will configure the window size - * to be capture in CCDC reg - */ -static void ccdc_setwin(struct v4l2_rect *image_win, - enum ccdc_frmfmt frm_fmt, - int ppc) -{ - int horz_start, horz_nr_pixels; - int vert_start, vert_nr_lines; - int val = 0, mid_img = 0; - - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); - /* - * ppc - per pixel count. indicates how many pixels per cell - * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. - * raw capture this is 1 - */ - horz_start = image_win->left << (ppc - 1); - horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; - regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, - CCDC_HORZ_INFO); - - vert_start = image_win->top; - - if (frm_fmt == CCDC_FRMFMT_INTERLACED) { - vert_nr_lines = (image_win->height >> 1) - 1; - vert_start >>= 1; - /* Since first line doesn't have any data */ - vert_start += 1; - /* configure VDINT0 */ - val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); - regw(val, CCDC_VDINT); - - } else { - /* Since first line doesn't have any data */ - vert_start += 1; - vert_nr_lines = image_win->height - 1; - /* - * configure VDINT0 and VDINT1. VDINT1 will be at half - * of image height - */ - mid_img = vert_start + (image_win->height / 2); - val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | - (mid_img & CCDC_VDINT_VDINT1_MASK); - regw(val, CCDC_VDINT); - - } - regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, - CCDC_VERT_START); - regw(vert_nr_lines, CCDC_VERT_LINES); - dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); -} - -static void ccdc_readregs(void) -{ - unsigned int val = 0; - - val = regr(CCDC_ALAW); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val); - val = regr(CCDC_CLAMP); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val); - val = regr(CCDC_DCSUB); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val); - val = regr(CCDC_BLKCMP); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val); - val = regr(CCDC_FPC_ADDR); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val); - val = regr(CCDC_FPC); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val); - val = regr(CCDC_FMTCFG); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val); - val = regr(CCDC_COLPTN); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val); - val = regr(CCDC_FMT_HORZ); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val); - val = regr(CCDC_FMT_VERT); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val); - val = regr(CCDC_HSIZE_OFF); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val); - val = regr(CCDC_SDOFST); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val); - val = regr(CCDC_VP_OUT); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val); - val = regr(CCDC_SYN_MODE); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val); - val = regr(CCDC_HORZ_INFO); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val); - val = regr(CCDC_VERT_START); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val); - val = regr(CCDC_VERT_LINES); - dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); -} - -static int ccdc_close(struct device *dev) -{ - return 0; -} - -/* - * ccdc_restore_defaults() - * This function will write defaults to all CCDC registers - */ -static void ccdc_restore_defaults(void) -{ - int i; - - /* disable CCDC */ - ccdc_enable(0); - /* set all registers to default value */ - for (i = 4; i <= 0x94; i += 4) - regw(0, i); - regw(CCDC_NO_CULLING, CCDC_CULLING); - regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); -} - -static int ccdc_open(struct device *device) -{ - ccdc_restore_defaults(); - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_enable_vport(1); - return 0; -} - -static void ccdc_sbl_reset(void) -{ - vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); -} - -/* - * ccdc_config_ycbcr() - * This function will configure CCDC for YCbCr video capture - */ -static void ccdc_config_ycbcr(void) -{ - struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; - u32 syn_mode; - - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); - /* - * first restore the CCDC registers to default values - * This is important since we assume default values to be set in - * a lot of registers that we didn't touch - */ - ccdc_restore_defaults(); - - /* - * configure pixel format, frame format, configure video frame - * format, enable output to SDRAM, enable internal timing generator - * and 8bit pack mode - */ - syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << - CCDC_SYN_MODE_INPMOD_SHIFT) | - ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << - CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | - CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); - - /* setup BT.656 sync mode */ - if (params->bt656_enable) { - regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); - - /* - * configure the FID, VD, HD pin polarity, - * fld,hd pol positive, vd negative, 8-bit data - */ - syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; - if (ccdc_cfg.if_type == VPFE_BT656_10BIT) - syn_mode |= CCDC_SYN_MODE_10BITS; - else - syn_mode |= CCDC_SYN_MODE_8BITS; - } else { - /* y/c external sync mode */ - syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << - CCDC_FID_POL_SHIFT) | - ((params->hd_pol & CCDC_HD_POL_MASK) << - CCDC_HD_POL_SHIFT) | - ((params->vd_pol & CCDC_VD_POL_MASK) << - CCDC_VD_POL_SHIFT)); - } - regw(syn_mode, CCDC_SYN_MODE); - - /* configure video window */ - ccdc_setwin(¶ms->win, params->frm_fmt, 2); - - /* - * configure the order of y cb cr in SDRAM, and disable latch - * internal register on vsync - */ - if (ccdc_cfg.if_type == VPFE_BT656_10BIT) - regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | - CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, - CCDC_CCDCFG); - else - regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | - CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); - - /* - * configure the horizontal line offset. This should be a - * on 32 byte boundary. So clear LSB 5 bits - */ - regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); - - /* configure the memory line offset */ - if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) - /* two fields are interleaved in memory */ - regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); - - ccdc_sbl_reset(); - dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); -} - -static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) -{ - u32 val; - - if (!bclamp->enable) { - /* configure DCSub */ - val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; - regw(val, CCDC_DCSUB); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val); - regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n"); - return; - } - /* - * Configure gain, Start pixel, No of line to be avg, - * No of pixel/line to be avg, & Enable the Black clamping - */ - val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | - ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << - CCDC_BLK_ST_PXL_SHIFT) | - ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << - CCDC_BLK_SAMPLE_LINE_SHIFT) | - ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << - CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); - regw(val, CCDC_CLAMP); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val); - /* If Black clamping is enable then make dcsub 0 */ - regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n"); -} - -static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) -{ - u32 val; - - val = ((bcomp->b & CCDC_BLK_COMP_MASK) | - ((bcomp->gb & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_GB_COMP_SHIFT) | - ((bcomp->gr & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_GR_COMP_SHIFT) | - ((bcomp->r & CCDC_BLK_COMP_MASK) << - CCDC_BLK_COMP_R_COMP_SHIFT)); - regw(val, CCDC_BLKCMP); -} - -/* - * ccdc_config_raw() - * This function will configure CCDC for Raw capture mode - */ -static void ccdc_config_raw(void) -{ - struct ccdc_params_raw *params = &ccdc_cfg.bayer; - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int syn_mode = 0; - unsigned int val; - - dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); - - /* Reset CCDC */ - ccdc_restore_defaults(); - - /* Disable latching function registers on VSYNC */ - regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); - - /* - * Configure the vertical sync polarity(SYN_MODE.VDPOL), - * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity - * (SYN_MODE.FLDPOL), frame format(progressive or interlace), - * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output - * SDRAM, enable internal timing generator - */ - syn_mode = - (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | - ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | - ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | - ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | - ((config_params->data_sz & CCDC_DATA_SZ_MASK) << - CCDC_DATA_SZ_SHIFT) | - ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | - CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); - - /* Enable and configure aLaw register if needed */ - if (config_params->alaw.enable) { - val = ((config_params->alaw.gamma_wd & - CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); - regw(val, CCDC_ALAW); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); - } - - /* Configure video window */ - ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); - - /* Configure Black Clamp */ - ccdc_config_black_clamp(&config_params->blk_clamp); - - /* Configure Black level compensation */ - ccdc_config_black_compense(&config_params->blk_comp); - - /* If data size is 8 bit then pack the data */ - if ((config_params->data_sz == CCDC_DATA_8BITS) || - config_params->alaw.enable) - syn_mode |= CCDC_DATA_PACK_ENABLE; - - /* disable video port */ - val = CCDC_DISABLE_VIDEO_PORT; - - if (config_params->data_sz == CCDC_DATA_8BITS) - val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) - << CCDC_FMTCFG_VPIN_SHIFT; - else - val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) - << CCDC_FMTCFG_VPIN_SHIFT; - /* Write value in FMTCFG */ - regw(val, CCDC_FMTCFG); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val); - /* Configure the color pattern according to mt9t001 sensor */ - regw(CCDC_COLPTN_VAL, CCDC_COLPTN); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); - /* - * Configure Data formatter(Video port) pixel selection - * (FMT_HORZ, FMT_VERT) - */ - val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << - CCDC_FMT_HORZ_FMTSPH_SHIFT) | - (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); - regw(val, CCDC_FMT_HORZ); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val); - val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) - << CCDC_FMT_VERT_FMTSLV_SHIFT; - if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) - val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; - else - val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; - - dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n", - params->win.height); - regw(val, CCDC_FMT_VERT); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val); - - dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)..."); - - /* - * Configure Horizontal offset register. If pack 8 is enabled then - * 1 pixel will take 1 byte - */ - if ((config_params->data_sz == CCDC_DATA_8BITS) || - config_params->alaw.enable) - regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & - CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); - else - /* else one pixel will take 2 byte */ - regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + - CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, - CCDC_HSIZE_OFF); - - /* Set value for SDOFST */ - if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { - if (params->image_invert_enable) { - /* For intelace inverse mode */ - regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n"); - } - - else { - /* For intelace non inverse mode */ - regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n"); - } - } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { - regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n"); - } - - /* - * Configure video port pixel selection (VPOUT) - * Here -1 is to make the height value less than FMT_VERT.FMTLNV - */ - if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) - val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) - << CCDC_VP_OUT_VERT_NUM_SHIFT; - else - val = - ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << - CCDC_VP_OUT_VERT_NUM_SHIFT; - - val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) - << CCDC_VP_OUT_HORZ_NUM_SHIFT; - val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; - regw(val, CCDC_VP_OUT); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val); - regw(syn_mode, CCDC_SYN_MODE); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); - - ccdc_sbl_reset(); - dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); - ccdc_readregs(); -} - -static int ccdc_configure(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_config_raw(); - else - ccdc_config_ycbcr(); - return 0; -} - -static int ccdc_set_buftype(enum ccdc_buftype buf_type) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.buf_type = buf_type; - else - ccdc_cfg.ycbcr.buf_type = buf_type; - return 0; -} - -static enum ccdc_buftype ccdc_get_buftype(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - return ccdc_cfg.bayer.buf_type; - return ccdc_cfg.ycbcr.buf_type; -} - -static int ccdc_enum_pix(u32 *pix, int i) -{ - int ret = -EINVAL; - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { - *pix = ccdc_raw_bayer_pix_formats[i]; - ret = 0; - } - } else { - if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { - *pix = ccdc_raw_yuv_pix_formats[i]; - ret = 0; - } - } - return ret; -} - -static int ccdc_set_pixel_format(u32 pixfmt) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; - if (pixfmt == V4L2_PIX_FMT_SBGGR8) - ccdc_cfg.bayer.config_params.alaw.enable = 1; - else if (pixfmt != V4L2_PIX_FMT_SBGGR16) - return -EINVAL; - } else { - if (pixfmt == V4L2_PIX_FMT_YUYV) - ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; - else if (pixfmt == V4L2_PIX_FMT_UYVY) - ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; - else - return -EINVAL; - } - return 0; -} - -static u32 ccdc_get_pixel_format(void) -{ - struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; - u32 pixfmt; - - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - if (alaw->enable) - pixfmt = V4L2_PIX_FMT_SBGGR8; - else - pixfmt = V4L2_PIX_FMT_SBGGR16; - else { - if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) - pixfmt = V4L2_PIX_FMT_YUYV; - else - pixfmt = V4L2_PIX_FMT_UYVY; - } - return pixfmt; -} - -static int ccdc_set_image_window(struct v4l2_rect *win) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.win = *win; - else - ccdc_cfg.ycbcr.win = *win; - return 0; -} - -static void ccdc_get_image_window(struct v4l2_rect *win) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - *win = ccdc_cfg.bayer.win; - else - *win = ccdc_cfg.ycbcr.win; -} - -static unsigned int ccdc_get_line_length(void) -{ - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int len; - - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { - if ((config_params->alaw.enable) || - (config_params->data_sz == CCDC_DATA_8BITS)) - len = ccdc_cfg.bayer.win.width; - else - len = ccdc_cfg.bayer.win.width * 2; - } else - len = ccdc_cfg.ycbcr.win.width * 2; - return ALIGN(len, 32); -} - -static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - ccdc_cfg.bayer.frm_fmt = frm_fmt; - else - ccdc_cfg.ycbcr.frm_fmt = frm_fmt; - return 0; -} - -static enum ccdc_frmfmt ccdc_get_frame_format(void) -{ - if (ccdc_cfg.if_type == VPFE_RAW_BAYER) - return ccdc_cfg.bayer.frm_fmt; - else - return ccdc_cfg.ycbcr.frm_fmt; -} - -static int ccdc_getfid(void) -{ - return (regr(CCDC_SYN_MODE) >> 15) & 1; -} - -/* misc operations */ -static inline void ccdc_setfbaddr(unsigned long addr) -{ - regw(addr & 0xffffffe0, CCDC_SDR_ADDR); -} - -static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) -{ - ccdc_cfg.if_type = params->if_type; - - switch (params->if_type) { - case VPFE_BT656: - case VPFE_YCBCR_SYNC_16: - case VPFE_YCBCR_SYNC_8: - case VPFE_BT656_10BIT: - ccdc_cfg.ycbcr.vd_pol = params->vdpol; - ccdc_cfg.ycbcr.hd_pol = params->hdpol; - break; - default: - /* TODO add support for raw bayer here */ - return -EINVAL; - } - return 0; -} - -static void ccdc_save_context(void) -{ - ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); - ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); - ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); - ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); - ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); - ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); - ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); - ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); - ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); - ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); - ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); - ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); - ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); - ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); - ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); - ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); - ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); - ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); - ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); - ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); - ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); - ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); - ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); - ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); - ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); - ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); - ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); - ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); - ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); - ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); - ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); - ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); - ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); - ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); - ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); - ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); - ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); -} - -static void ccdc_restore_context(void) -{ - regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); - regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); - regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); - regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); - regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); - regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); - regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); - regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); - regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); - regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); - regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); - regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); - regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); - regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); - regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); - regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); - regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); - regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); - regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); - regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); - regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); - regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); - regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); - regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); - regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); - regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); - regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); - regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); - regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); - regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); - regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); - regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); - regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); - regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); - regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); - regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); - regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); -} -static const struct ccdc_hw_device ccdc_hw_dev = { - .name = "DM6446 CCDC", - .owner = THIS_MODULE, - .hw_ops = { - .open = ccdc_open, - .close = ccdc_close, - .reset = ccdc_sbl_reset, - .enable = ccdc_enable, - .set_hw_if_params = ccdc_set_hw_if_params, - .configure = ccdc_configure, - .set_buftype = ccdc_set_buftype, - .get_buftype = ccdc_get_buftype, - .enum_pix = ccdc_enum_pix, - .set_pixel_format = ccdc_set_pixel_format, - .get_pixel_format = ccdc_get_pixel_format, - .set_frame_format = ccdc_set_frame_format, - .get_frame_format = ccdc_get_frame_format, - .set_image_window = ccdc_set_image_window, - .get_image_window = ccdc_get_image_window, - .get_line_length = ccdc_get_line_length, - .setfbaddr = ccdc_setfbaddr, - .getfid = ccdc_getfid, - }, -}; - -static int dm644x_ccdc_probe(struct platform_device *pdev) -{ - struct resource *res; - int status = 0; - - /* - * first try to register with vpfe. If not correct platform, then we - * don't have to iomap - */ - status = vpfe_register_ccdc_device(&ccdc_hw_dev); - if (status < 0) - return status; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - status = -ENODEV; - goto fail_nores; - } - - res = request_mem_region(res->start, resource_size(res), res->name); - if (!res) { - status = -EBUSY; - goto fail_nores; - } - - ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); - if (!ccdc_cfg.base_addr) { - status = -ENOMEM; - goto fail_nomem; - } - - ccdc_cfg.dev = &pdev->dev; - printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); - return 0; -fail_nomem: - release_mem_region(res->start, resource_size(res)); -fail_nores: - vpfe_unregister_ccdc_device(&ccdc_hw_dev); - return status; -} - -static int dm644x_ccdc_remove(struct platform_device *pdev) -{ - struct resource *res; - - iounmap(ccdc_cfg.base_addr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - vpfe_unregister_ccdc_device(&ccdc_hw_dev); - return 0; -} - -static int dm644x_ccdc_suspend(struct device *dev) -{ - /* Save CCDC context */ - ccdc_save_context(); - /* Disable CCDC */ - ccdc_enable(0); - - return 0; -} - -static int dm644x_ccdc_resume(struct device *dev) -{ - /* Restore CCDC context */ - ccdc_restore_context(); - - return 0; -} - -static const struct dev_pm_ops dm644x_ccdc_pm_ops = { - .suspend = dm644x_ccdc_suspend, - .resume = dm644x_ccdc_resume, -}; - -static struct platform_driver dm644x_ccdc_driver = { - .driver = { - .name = "dm644x_ccdc", - .pm = &dm644x_ccdc_pm_ops, - }, - .remove = dm644x_ccdc_remove, - .probe = dm644x_ccdc_probe, -}; - -module_platform_driver(dm644x_ccdc_driver); diff --git a/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h deleted file mode 100644 index c4894f6a254e..000000000000 --- a/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2006-2009 Texas Instruments Inc - */ -#ifndef _DM644X_CCDC_REGS_H -#define _DM644X_CCDC_REGS_H - -/**************************************************************************\ -* Register OFFSET Definitions -\**************************************************************************/ -#define CCDC_PID 0x0 -#define CCDC_PCR 0x4 -#define CCDC_SYN_MODE 0x8 -#define CCDC_HD_VD_WID 0xc -#define CCDC_PIX_LINES 0x10 -#define CCDC_HORZ_INFO 0x14 -#define CCDC_VERT_START 0x18 -#define CCDC_VERT_LINES 0x1c -#define CCDC_CULLING 0x20 -#define CCDC_HSIZE_OFF 0x24 -#define CCDC_SDOFST 0x28 -#define CCDC_SDR_ADDR 0x2c -#define CCDC_CLAMP 0x30 -#define CCDC_DCSUB 0x34 -#define CCDC_COLPTN 0x38 -#define CCDC_BLKCMP 0x3c -#define CCDC_FPC 0x40 -#define CCDC_FPC_ADDR 0x44 -#define CCDC_VDINT 0x48 -#define CCDC_ALAW 0x4c -#define CCDC_REC656IF 0x50 -#define CCDC_CCDCFG 0x54 -#define CCDC_FMTCFG 0x58 -#define CCDC_FMT_HORZ 0x5c -#define CCDC_FMT_VERT 0x60 -#define CCDC_FMT_ADDR0 0x64 -#define CCDC_FMT_ADDR1 0x68 -#define CCDC_FMT_ADDR2 0x6c -#define CCDC_FMT_ADDR3 0x70 -#define CCDC_FMT_ADDR4 0x74 -#define CCDC_FMT_ADDR5 0x78 -#define CCDC_FMT_ADDR6 0x7c -#define CCDC_FMT_ADDR7 0x80 -#define CCDC_PRGEVEN_0 0x84 -#define CCDC_PRGEVEN_1 0x88 -#define CCDC_PRGODD_0 0x8c -#define CCDC_PRGODD_1 0x90 -#define CCDC_VP_OUT 0x94 -#define CCDC_REG_END 0x98 - -/*************************************************************** -* Define for various register bit mask and shifts for CCDC -****************************************************************/ -#define CCDC_FID_POL_MASK 1 -#define CCDC_FID_POL_SHIFT 4 -#define CCDC_HD_POL_MASK 1 -#define CCDC_HD_POL_SHIFT 3 -#define CCDC_VD_POL_MASK 1 -#define CCDC_VD_POL_SHIFT 2 -#define CCDC_HSIZE_OFF_MASK 0xffffffe0 -#define CCDC_32BYTE_ALIGN_VAL 31 -#define CCDC_FRM_FMT_MASK 0x1 -#define CCDC_FRM_FMT_SHIFT 7 -#define CCDC_DATA_SZ_MASK 7 -#define CCDC_DATA_SZ_SHIFT 8 -#define CCDC_PIX_FMT_MASK 3 -#define CCDC_PIX_FMT_SHIFT 12 -#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF -#define CCDC_WEN_ENABLE BIT(17) -#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF -#define CCDC_VDHDEN_ENABLE BIT(16) -#define CCDC_LPF_ENABLE BIT(14) -#define CCDC_ALAW_ENABLE BIT(3) -#define CCDC_ALAW_GAMMA_WD_MASK 7 -#define CCDC_BLK_CLAMP_ENABLE BIT(31) -#define CCDC_BLK_SGAIN_MASK 0x1F -#define CCDC_BLK_ST_PXL_MASK 0x7FFF -#define CCDC_BLK_ST_PXL_SHIFT 10 -#define CCDC_BLK_SAMPLE_LN_MASK 7 -#define CCDC_BLK_SAMPLE_LN_SHIFT 28 -#define CCDC_BLK_SAMPLE_LINE_MASK 7 -#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 -#define CCDC_BLK_DC_SUB_MASK 0x03FFF -#define CCDC_BLK_COMP_MASK 0xFF -#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 -#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 -#define CCDC_BLK_COMP_R_COMP_SHIFT 24 -#define CCDC_LATCH_ON_VSYNC_DISABLE BIT(15) -#define CCDC_FPC_ENABLE BIT(15) -#define CCDC_FPC_DISABLE 0 -#define CCDC_FPC_FPC_NUM_MASK 0x7FFF -#define CCDC_DATA_PACK_ENABLE BIT(11) -#define CCDC_FMTCFG_VPIN_MASK 7 -#define CCDC_FMTCFG_VPIN_SHIFT 12 -#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF -#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF -#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 -#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF -#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF -#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 -#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF -#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 -#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF -#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 -#define CCDC_VP_OUT_HORZ_ST_MASK 0xF -#define CCDC_HORZ_INFO_SPH_SHIFT 16 -#define CCDC_VERT_START_SLV0_SHIFT 16 -#define CCDC_VDINT_VDINT0_SHIFT 16 -#define CCDC_VDINT_VDINT1_MASK 0xFFFF -#define CCDC_PPC_RAW 1 -#define CCDC_DCSUB_DEFAULT_VAL 0 -#define CCDC_CLAMP_DEFAULT_VAL 0 -#define CCDC_ENABLE_VIDEO_PORT 0x8000 -#define CCDC_DISABLE_VIDEO_PORT 0 -#define CCDC_COLPTN_VAL 0xBB11BB11 -#define CCDC_TWO_BYTES_PER_PIXEL 2 -#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D -#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 -#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 -#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 -#define CCDC_INTERLACED_HEIGHT_SHIFT 1 -#define CCDC_SYN_MODE_INPMOD_SHIFT 12 -#define CCDC_SYN_MODE_INPMOD_MASK 3 -#define CCDC_SYN_MODE_8BITS (7 << 8) -#define CCDC_SYN_MODE_10BITS (6 << 8) -#define CCDC_SYN_MODE_11BITS (5 << 8) -#define CCDC_SYN_MODE_12BITS (4 << 8) -#define CCDC_SYN_MODE_13BITS (3 << 8) -#define CCDC_SYN_MODE_14BITS (2 << 8) -#define CCDC_SYN_MODE_15BITS (1 << 8) -#define CCDC_SYN_MODE_16BITS (0 << 8) -#define CCDC_SYN_FLDMODE_MASK 1 -#define CCDC_SYN_FLDMODE_SHIFT 7 -#define CCDC_REC656IF_BT656_EN 3 -#define CCDC_SYN_MODE_VD_POL_NEGATIVE BIT(2) -#define CCDC_CCDCFG_Y8POS_SHIFT 11 -#define CCDC_CCDCFG_BW656_10BIT BIT(5) -#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 -#define CCDC_NO_CULLING 0xffff00ff -#endif diff --git a/drivers/media/platform/ti/davinci/isif.c b/drivers/media/platform/ti/davinci/isif.c deleted file mode 100644 index 69e862de014f..000000000000 --- a/drivers/media/platform/ti/davinci/isif.c +++ /dev/null @@ -1,1127 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * Image Sensor Interface (ISIF) driver - * - * This driver is for configuring the ISIF IP available on DM365 or any other - * TI SoCs. This is used for capturing yuv or bayer video or image data - * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 - * and DM6446, but with enhanced or additional ip blocks. The driver - * configures the ISIF upon commands from the vpfe bridge driver through - * ccdc_hw_device interface. - * - * TODO: 1) Raw bayer parameter settings and bayer capture - * 2) Add support for control ioctl - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "isif_regs.h" -#include "ccdc_hw_device.h" - -/* Defaults for module configuration parameters */ -static const struct isif_config_params_raw isif_config_defaults = { - .linearize = { - .en = 0, - .corr_shft = ISIF_NO_SHIFT, - .scale_fact = {1, 0}, - }, - .df_csc = { - .df_or_csc = 0, - .csc = { - .en = 0, - }, - }, - .dfc = { - .en = 0, - }, - .bclamp = { - .en = 0, - }, - .gain_offset = { - .gain = { - .r_ye = {1, 0}, - .gr_cy = {1, 0}, - .gb_g = {1, 0}, - .b_mg = {1, 0}, - }, - }, - .culling = { - .hcpat_odd = 0xff, - .hcpat_even = 0xff, - .vcpat = 0xff, - }, - .compress = { - .alg = ISIF_ALAW, - }, -}; - -/* ISIF operation configuration */ -static struct isif_oper_config { - struct device *dev; - enum vpfe_hw_if_type if_type; - struct isif_ycbcr_config ycbcr; - struct isif_params_raw bayer; - enum isif_data_pack data_pack; - /* ISIF base address */ - void __iomem *base_addr; - /* ISIF Linear Table 0 */ - void __iomem *linear_tbl0_addr; - /* ISIF Linear Table 1 */ - void __iomem *linear_tbl1_addr; -} isif_cfg = { - .ycbcr = { - .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, - .frm_fmt = CCDC_FRMFMT_INTERLACED, - .win = ISIF_WIN_NTSC, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .pix_order = CCDC_PIXORDER_CBYCRY, - .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, - }, - .bayer = { - .pix_fmt = CCDC_PIXFMT_RAW, - .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, - .win = ISIF_WIN_VGA, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .gain = { - .r_ye = {1, 0}, - .gr_cy = {1, 0}, - .gb_g = {1, 0}, - .b_mg = {1, 0}, - }, - .cfa_pat = ISIF_CFA_PAT_MOSAIC, - .data_msb = ISIF_BIT_MSB_11, - .config_params = { - .data_shift = ISIF_NO_SHIFT, - .col_pat_field0 = { - .olop = ISIF_GREEN_BLUE, - .olep = ISIF_BLUE, - .elop = ISIF_RED, - .elep = ISIF_GREEN_RED, - }, - .col_pat_field1 = { - .olop = ISIF_GREEN_BLUE, - .olep = ISIF_BLUE, - .elop = ISIF_RED, - .elep = ISIF_GREEN_RED, - }, - .test_pat_gen = 0, - }, - }, - .data_pack = ISIF_DATA_PACK8, -}; - -/* Raw Bayer formats */ -static const u32 isif_raw_bayer_pix_formats[] = { - V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; - -/* Raw YUV formats */ -static const u32 isif_raw_yuv_pix_formats[] = { - V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; - -/* register access routines */ -static inline u32 regr(u32 offset) -{ - return __raw_readl(isif_cfg.base_addr + offset); -} - -static inline void regw(u32 val, u32 offset) -{ - __raw_writel(val, isif_cfg.base_addr + offset); -} - -/* reg_modify() - read, modify and write register */ -static inline u32 reg_modify(u32 mask, u32 val, u32 offset) -{ - u32 new_val = (regr(offset) & ~mask) | (val & mask); - - regw(new_val, offset); - return new_val; -} - -static inline void regw_lin_tbl(u32 val, u32 offset, int i) -{ - if (!i) - __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); - else - __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); -} - -static void isif_disable_all_modules(void) -{ - /* disable BC */ - regw(0, CLAMPCFG); - /* disable vdfc */ - regw(0, DFCCTL); - /* disable CSC */ - regw(0, CSCCTL); - /* disable linearization */ - regw(0, LINCFG0); - /* disable other modules here as they are supported */ -} - -static void isif_enable(int en) -{ - if (!en) { - /* Before disable isif, disable all ISIF modules */ - isif_disable_all_modules(); - /* - * wait for next VD. Assume lowest scan rate is 12 Hz. So - * 100 msec delay is good enough - */ - msleep(100); - } - reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); -} - -static void isif_enable_output_to_sdram(int en) -{ - reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); -} - -static void isif_config_culling(struct isif_cul *cul) -{ - u32 val; - - /* Horizontal pattern */ - val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; - regw(val, CULH); - - /* vertical pattern */ - regw(cul->vcpat, CULV); - - /* LPF */ - reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, - cul->en_lpf << ISIF_LPF_SHIFT, MODESET); -} - -static void isif_config_gain_offset(void) -{ - struct isif_gain_offsets_adj *gain_off_p = - &isif_cfg.bayer.config_params.gain_offset; - u32 val; - - val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | - (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | - (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | - (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | - (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | - (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); - - reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); - - val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | - gain_off_p->gain.r_ye.decimal; - regw(val, CRGAIN); - - val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | - gain_off_p->gain.gr_cy.decimal; - regw(val, CGRGAIN); - - val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | - gain_off_p->gain.gb_g.decimal; - regw(val, CGBGAIN); - - val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | - gain_off_p->gain.b_mg.decimal; - regw(val, CBGAIN); - - regw(gain_off_p->offset, COFSTA); -} - -static void isif_restore_defaults(void) -{ - enum vpss_ccdc_source_sel source = VPSS_CCDCIN; - - dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); - isif_cfg.bayer.config_params = isif_config_defaults; - /* Enable clock to ISIF, IPIPEIF and BL */ - vpss_enable_clock(VPSS_CCDC_CLOCK, 1); - vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); - vpss_enable_clock(VPSS_BL_CLOCK, 1); - /* Set default offset and gain */ - isif_config_gain_offset(); - vpss_select_ccdc_source(source); - dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); -} - -static int isif_open(struct device *device) -{ - isif_restore_defaults(); - return 0; -} - -/* This function will configure the window size to be capture in ISIF reg */ -static void isif_setwin(struct v4l2_rect *image_win, - enum ccdc_frmfmt frm_fmt, int ppc) -{ - int horz_start, horz_nr_pixels; - int vert_start, vert_nr_lines; - int mid_img = 0; - - dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); - /* - * ppc - per pixel count. indicates how many pixels per cell - * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. - * raw capture this is 1 - */ - horz_start = image_win->left << (ppc - 1); - horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; - - /* Writing the horizontal info into the registers */ - regw(horz_start & START_PX_HOR_MASK, SPH); - regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); - vert_start = image_win->top; - - if (frm_fmt == CCDC_FRMFMT_INTERLACED) { - vert_nr_lines = (image_win->height >> 1) - 1; - vert_start >>= 1; - /* To account for VD since line 0 doesn't have any data */ - vert_start += 1; - } else { - /* To account for VD since line 0 doesn't have any data */ - vert_start += 1; - vert_nr_lines = image_win->height - 1; - /* configure VDINT0 and VDINT1 */ - mid_img = vert_start + (image_win->height / 2); - regw(mid_img, VDINT1); - } - - regw(0, VDINT0); - regw(vert_start & START_VER_ONE_MASK, SLV0); - regw(vert_start & START_VER_TWO_MASK, SLV1); - regw(vert_nr_lines & NUM_LINES_VER, LNV); -} - -static void isif_config_bclamp(struct isif_black_clamp *bc) -{ - u32 val; - - /* - * DC Offset is always added to image data irrespective of bc enable - * status - */ - regw(bc->dc_offset, CLDCOFST); - - if (bc->en) { - val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; - - /* Enable BC and horizontal clamp calculation parameters */ - val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); - - regw(val, CLAMPCFG); - - if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { - /* - * Window count for calculation - * Base window selection - * pixel limit - * Horizontal size of window - * vertical size of the window - * Horizontal start position of the window - * Vertical start position of the window - */ - val = bc->horz.win_count_calc | - ((!!bc->horz.base_win_sel_calc) << - ISIF_HORZ_BC_WIN_SEL_SHIFT) | - ((!!bc->horz.clamp_pix_limit) << - ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | - (bc->horz.win_h_sz_calc << - ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | - (bc->horz.win_v_sz_calc << - ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); - regw(val, CLHWIN0); - - regw(bc->horz.win_start_h_calc, CLHWIN1); - regw(bc->horz.win_start_v_calc, CLHWIN2); - } - - /* vertical clamp calculation parameters */ - - /* Reset clamp value sel for previous line */ - val |= - (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | - (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); - regw(val, CLVWIN0); - - /* Optical Black horizontal start position */ - regw(bc->vert.ob_start_h, CLVWIN1); - /* Optical Black vertical start position */ - regw(bc->vert.ob_start_v, CLVWIN2); - /* Optical Black vertical size for calculation */ - regw(bc->vert.ob_v_sz_calc, CLVWIN3); - /* Vertical start position for BC subtraction */ - regw(bc->vert_start_sub, CLSV); - } -} - -static void isif_config_linearization(struct isif_linearize *linearize) -{ - u32 val, i; - - if (!linearize->en) { - regw(0, LINCFG0); - return; - } - - /* shift value for correction & enable linearization (set lsb) */ - val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; - regw(val, LINCFG0); - - /* Scale factor */ - val = ((!!linearize->scale_fact.integer) << - ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | - linearize->scale_fact.decimal; - regw(val, LINCFG1); - - for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { - if (i % 2) - regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); - else - regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); - } -} - -static int isif_config_dfc(struct isif_dfc *vdfc) -{ - /* initialize retries to loop for max ~ 250 usec */ - u32 val, count, retries = loops_per_jiffy / (4000/HZ); - int i; - - if (!vdfc->en) - return 0; - - /* Correction mode */ - val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); - - /* Correct whole line or partial */ - if (vdfc->corr_whole_line) - val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; - - /* level shift value */ - val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; - - regw(val, DFCCTL); - - /* Defect saturation level */ - regw(vdfc->def_sat_level, VDFSATLV); - - regw(vdfc->table[0].pos_vert, DFCMEM0); - regw(vdfc->table[0].pos_horz, DFCMEM1); - if (vdfc->corr_mode == ISIF_VDFC_NORMAL || - vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { - regw(vdfc->table[0].level_at_pos, DFCMEM2); - regw(vdfc->table[0].level_up_pixels, DFCMEM3); - regw(vdfc->table[0].level_low_pixels, DFCMEM4); - } - - /* set DFCMARST and set DFCMWR */ - val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; - regw(val, DFCMEMCTL); - - count = retries; - while (count && (regr(DFCMEMCTL) & 0x1)) - count--; - - if (!count) { - dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); - return -1; - } - - for (i = 1; i < vdfc->num_vdefects; i++) { - regw(vdfc->table[i].pos_vert, DFCMEM0); - regw(vdfc->table[i].pos_horz, DFCMEM1); - if (vdfc->corr_mode == ISIF_VDFC_NORMAL || - vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { - regw(vdfc->table[i].level_at_pos, DFCMEM2); - regw(vdfc->table[i].level_up_pixels, DFCMEM3); - regw(vdfc->table[i].level_low_pixels, DFCMEM4); - } - val = regr(DFCMEMCTL); - /* clear DFCMARST and set DFCMWR */ - val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); - val |= 1; - regw(val, DFCMEMCTL); - - count = retries; - while (count && (regr(DFCMEMCTL) & 0x1)) - count--; - - if (!count) { - dev_err(isif_cfg.dev, - "defect table write timeout !!!\n"); - return -1; - } - } - if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { - /* Extra cycle needed */ - regw(0, DFCMEM0); - regw(0x1FFF, DFCMEM1); - regw(1, DFCMEMCTL); - } - - /* enable VDFC */ - reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), - DFCCTL); - return 0; -} - -static void isif_config_csc(struct isif_df_csc *df_csc) -{ - u32 val1 = 0, val2 = 0, i; - - if (!df_csc->csc.en) { - regw(0, CSCCTL); - return; - } - for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { - if ((i % 2) == 0) { - /* CSCM - LSB */ - val1 = (df_csc->csc.coeff[i].integer << - ISIF_CSC_COEF_INTEG_SHIFT) | - df_csc->csc.coeff[i].decimal; - } else { - - /* CSCM - MSB */ - val2 = (df_csc->csc.coeff[i].integer << - ISIF_CSC_COEF_INTEG_SHIFT) | - df_csc->csc.coeff[i].decimal; - val2 <<= ISIF_CSCM_MSB_SHIFT; - val2 |= val1; - regw(val2, (CSCM0 + ((i - 1) << 1))); - } - } - - /* program the active area */ - regw(df_csc->start_pix, FMTSPH); - /* - * one extra pixel as required for CSC. Actually number of - * pixel - 1 should be configured in this register. So we - * need to subtract 1 before writing to FMTSPH, but we will - * not do this since csc requires one extra pixel - */ - regw(df_csc->num_pixels, FMTLNH); - regw(df_csc->start_line, FMTSLV); - /* - * one extra line as required for CSC. See reason documented for - * num_pixels - */ - regw(df_csc->num_lines, FMTLNV); - - /* Enable CSC */ - regw(1, CSCCTL); -} - -static int isif_config_raw(void) -{ - struct isif_params_raw *params = &isif_cfg.bayer; - struct isif_config_params_raw *module_params = - &isif_cfg.bayer.config_params; - struct vpss_pg_frame_size frame_size; - struct vpss_sync_pol sync; - u32 val; - - dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); - - /* - * Configure CCDCFG register:- - * Set CCD Not to swap input since input is RAW data - * Set FID detection function to Latch at V-Sync - * Set WENLOG - isif valid area - * Set TRGSEL - * Set EXTRG - * Packed to 8 or 16 bits - */ - - val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | - ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | - ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; - - dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); - regw(val, CCDCFG); - - /* - * Configure the vertical sync polarity(MODESET.VDPOL) - * Configure the horizontal sync polarity (MODESET.HDPOL) - * Configure frame id polarity (MODESET.FLDPOL) - * Configure data polarity - * Configure External WEN Selection - * Configure frame format(progressive or interlace) - * Configure pixel format (Input mode) - * Configure the data shift - */ - - val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | - (params->hd_pol << ISIF_HD_POL_SHIFT) | - (params->fid_pol << ISIF_FID_POL_SHIFT) | - (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | - (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | - (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | - (params->pix_fmt << ISIF_INPUT_SHIFT) | - (params->config_params.data_shift << ISIF_DATASFT_SHIFT); - - regw(val, MODESET); - dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); - - /* - * Configure GAMMAWD register - * CFA pattern setting - */ - val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; - - /* Gamma msb */ - if (module_params->compress.alg == ISIF_ALAW) - val |= ISIF_ALAW_ENABLE; - - val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); - regw(val, CGAMMAWD); - - /* Configure DPCM compression settings */ - if (module_params->compress.alg == ISIF_DPCM) { - val = BIT(ISIF_DPCM_EN_SHIFT) | - (module_params->compress.pred << - ISIF_DPCM_PREDICTOR_SHIFT); - } - - regw(val, MISC); - - /* Configure Gain & Offset */ - isif_config_gain_offset(); - - /* Configure Color pattern */ - val = (params->config_params.col_pat_field0.olop) | - (params->config_params.col_pat_field0.olep << 2) | - (params->config_params.col_pat_field0.elop << 4) | - (params->config_params.col_pat_field0.elep << 6) | - (params->config_params.col_pat_field1.olop << 8) | - (params->config_params.col_pat_field1.olep << 10) | - (params->config_params.col_pat_field1.elop << 12) | - (params->config_params.col_pat_field1.elep << 14); - regw(val, CCOLP); - dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); - - /* Configure HSIZE register */ - val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; - - /* calculate line offset in 32 bytes based on pack value */ - if (isif_cfg.data_pack == ISIF_PACK_8BIT) - val |= ((params->win.width + 31) >> 5); - else if (isif_cfg.data_pack == ISIF_PACK_12BIT) - val |= (((params->win.width + - (params->win.width >> 2)) + 31) >> 5); - else - val |= (((params->win.width * 2) + 31) >> 5); - regw(val, HSIZE); - - /* Configure SDOFST register */ - if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { - if (params->image_invert_en) { - /* For interlace inverse mode */ - regw(0x4B6D, SDOFST); - dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); - } else { - /* For interlace non inverse mode */ - regw(0x0B6D, SDOFST); - dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); - } - } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { - if (params->image_invert_en) { - /* For progressive inverse mode */ - regw(0x4000, SDOFST); - dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); - } else { - /* For progressive non inverse mode */ - regw(0x0000, SDOFST); - dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); - } - } - - /* Configure video window */ - isif_setwin(¶ms->win, params->frm_fmt, 1); - - /* Configure Black Clamp */ - isif_config_bclamp(&module_params->bclamp); - - /* Configure Vertical Defection Pixel Correction */ - if (isif_config_dfc(&module_params->dfc) < 0) - return -EFAULT; - - if (!module_params->df_csc.df_or_csc) - /* Configure Color Space Conversion */ - isif_config_csc(&module_params->df_csc); - - isif_config_linearization(&module_params->linearize); - - /* Configure Culling */ - isif_config_culling(&module_params->culling); - - /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ - regw(module_params->horz_offset, DATAHOFST); - regw(module_params->vert_offset, DATAVOFST); - - /* Setup test pattern if enabled */ - if (params->config_params.test_pat_gen) { - /* Use the HD/VD pol settings from user */ - sync.ccdpg_hdpol = params->hd_pol; - sync.ccdpg_vdpol = params->vd_pol; - dm365_vpss_set_sync_pol(sync); - frame_size.hlpfr = isif_cfg.bayer.win.width; - frame_size.pplen = isif_cfg.bayer.win.height; - dm365_vpss_set_pg_frame_size(frame_size); - vpss_select_ccdc_source(VPSS_PGLPBK); - } - - dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); - return 0; -} - -static int isif_set_buftype(enum ccdc_buftype buf_type) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - isif_cfg.bayer.buf_type = buf_type; - else - isif_cfg.ycbcr.buf_type = buf_type; - - return 0; - -} -static enum ccdc_buftype isif_get_buftype(void) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - return isif_cfg.bayer.buf_type; - - return isif_cfg.ycbcr.buf_type; -} - -static int isif_enum_pix(u32 *pix, int i) -{ - int ret = -EINVAL; - - if (isif_cfg.if_type == VPFE_RAW_BAYER) { - if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { - *pix = isif_raw_bayer_pix_formats[i]; - ret = 0; - } - } else { - if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { - *pix = isif_raw_yuv_pix_formats[i]; - ret = 0; - } - } - - return ret; -} - -static int isif_set_pixel_format(unsigned int pixfmt) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) { - if (pixfmt == V4L2_PIX_FMT_SBGGR8) { - if ((isif_cfg.bayer.config_params.compress.alg != - ISIF_ALAW) && - (isif_cfg.bayer.config_params.compress.alg != - ISIF_DPCM)) { - dev_dbg(isif_cfg.dev, - "Either configure A-Law or DPCM\n"); - return -EINVAL; - } - isif_cfg.data_pack = ISIF_PACK_8BIT; - } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { - isif_cfg.bayer.config_params.compress.alg = - ISIF_NO_COMPRESSION; - isif_cfg.data_pack = ISIF_PACK_16BIT; - } else - return -EINVAL; - isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; - } else { - if (pixfmt == V4L2_PIX_FMT_YUYV) - isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; - else if (pixfmt == V4L2_PIX_FMT_UYVY) - isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; - else - return -EINVAL; - isif_cfg.data_pack = ISIF_PACK_8BIT; - } - return 0; -} - -static u32 isif_get_pixel_format(void) -{ - u32 pixfmt; - - if (isif_cfg.if_type == VPFE_RAW_BAYER) - if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || - isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) - pixfmt = V4L2_PIX_FMT_SBGGR8; - else - pixfmt = V4L2_PIX_FMT_SBGGR16; - else { - if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) - pixfmt = V4L2_PIX_FMT_YUYV; - else - pixfmt = V4L2_PIX_FMT_UYVY; - } - return pixfmt; -} - -static int isif_set_image_window(struct v4l2_rect *win) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) { - isif_cfg.bayer.win.top = win->top; - isif_cfg.bayer.win.left = win->left; - isif_cfg.bayer.win.width = win->width; - isif_cfg.bayer.win.height = win->height; - } else { - isif_cfg.ycbcr.win.top = win->top; - isif_cfg.ycbcr.win.left = win->left; - isif_cfg.ycbcr.win.width = win->width; - isif_cfg.ycbcr.win.height = win->height; - } - return 0; -} - -static void isif_get_image_window(struct v4l2_rect *win) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - *win = isif_cfg.bayer.win; - else - *win = isif_cfg.ycbcr.win; -} - -static unsigned int isif_get_line_length(void) -{ - unsigned int len; - - if (isif_cfg.if_type == VPFE_RAW_BAYER) { - if (isif_cfg.data_pack == ISIF_PACK_8BIT) - len = ((isif_cfg.bayer.win.width)); - else if (isif_cfg.data_pack == ISIF_PACK_12BIT) - len = (((isif_cfg.bayer.win.width * 2) + - (isif_cfg.bayer.win.width >> 2))); - else - len = (((isif_cfg.bayer.win.width * 2))); - } else - len = (((isif_cfg.ycbcr.win.width * 2))); - return ALIGN(len, 32); -} - -static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - isif_cfg.bayer.frm_fmt = frm_fmt; - else - isif_cfg.ycbcr.frm_fmt = frm_fmt; - return 0; -} -static enum ccdc_frmfmt isif_get_frame_format(void) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - return isif_cfg.bayer.frm_fmt; - return isif_cfg.ycbcr.frm_fmt; -} - -static int isif_getfid(void) -{ - return (regr(MODESET) >> 15) & 0x1; -} - -/* misc operations */ -static void isif_setfbaddr(unsigned long addr) -{ - regw((addr >> 21) & 0x07ff, CADU); - regw((addr >> 5) & 0x0ffff, CADL); -} - -static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) -{ - isif_cfg.if_type = params->if_type; - - switch (params->if_type) { - case VPFE_BT656: - case VPFE_BT656_10BIT: - case VPFE_YCBCR_SYNC_8: - isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; - isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; - break; - case VPFE_BT1120: - case VPFE_YCBCR_SYNC_16: - isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; - isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; - break; - case VPFE_RAW_BAYER: - isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; - break; - default: - dev_dbg(isif_cfg.dev, "Invalid interface type\n"); - return -EINVAL; - } - - return 0; -} - -/* This function will configure ISIF for YCbCr parameters. */ -static int isif_config_ycbcr(void) -{ - struct isif_ycbcr_config *params = &isif_cfg.ycbcr; - u32 modeset = 0, ccdcfg = 0; - - dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); - - /* configure pixel format or input mode */ - modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | - (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | - (params->fid_pol << ISIF_FID_POL_SHIFT) | - (params->hd_pol << ISIF_HD_POL_SHIFT) | - (params->vd_pol << ISIF_VD_POL_SHIFT); - - /* pack the data to 8-bit ISIFCFG */ - switch (isif_cfg.if_type) { - case VPFE_BT656: - if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { - dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); - return -EINVAL; - } - modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); - regw(3, REC656IF); - ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; - break; - case VPFE_BT656_10BIT: - if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { - dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); - return -EINVAL; - } - /* setup BT.656, embedded sync */ - regw(3, REC656IF); - /* enable 10 bit mode in ccdcfg */ - ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | - ISIF_BW656_ENABLE; - break; - case VPFE_BT1120: - if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { - dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); - return -EINVAL; - } - regw(3, REC656IF); - break; - - case VPFE_YCBCR_SYNC_8: - ccdcfg |= ISIF_DATA_PACK8; - ccdcfg |= ISIF_YCINSWP_YCBCR; - if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { - dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); - return -EINVAL; - } - break; - case VPFE_YCBCR_SYNC_16: - if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { - dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); - return -EINVAL; - } - break; - default: - /* should never come here */ - dev_dbg(isif_cfg.dev, "Invalid interface type\n"); - return -EINVAL; - } - - regw(modeset, MODESET); - - /* Set up pix order */ - ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; - - regw(ccdcfg, CCDCFG); - - /* configure video window */ - if ((isif_cfg.if_type == VPFE_BT1120) || - (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) - isif_setwin(¶ms->win, params->frm_fmt, 1); - else - isif_setwin(¶ms->win, params->frm_fmt, 2); - - /* - * configure the horizontal line offset - * this is done by rounding up width to a multiple of 16 pixels - * and multiply by two to account for y:cb:cr 4:2:2 data - */ - regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); - - /* configure the memory line offset */ - if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && - (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) - /* two fields are interleaved in memory */ - regw(0x00000249, SDOFST); - - return 0; -} - -static int isif_configure(void) -{ - if (isif_cfg.if_type == VPFE_RAW_BAYER) - return isif_config_raw(); - return isif_config_ycbcr(); -} - -static int isif_close(struct device *device) -{ - /* copy defaults to module params */ - isif_cfg.bayer.config_params = isif_config_defaults; - return 0; -} - -static const struct ccdc_hw_device isif_hw_dev = { - .name = "ISIF", - .owner = THIS_MODULE, - .hw_ops = { - .open = isif_open, - .close = isif_close, - .enable = isif_enable, - .enable_out_to_sdram = isif_enable_output_to_sdram, - .set_hw_if_params = isif_set_hw_if_params, - .configure = isif_configure, - .set_buftype = isif_set_buftype, - .get_buftype = isif_get_buftype, - .enum_pix = isif_enum_pix, - .set_pixel_format = isif_set_pixel_format, - .get_pixel_format = isif_get_pixel_format, - .set_frame_format = isif_set_frame_format, - .get_frame_format = isif_get_frame_format, - .set_image_window = isif_set_image_window, - .get_image_window = isif_get_image_window, - .get_line_length = isif_get_line_length, - .setfbaddr = isif_setfbaddr, - .getfid = isif_getfid, - }, -}; - -static int isif_probe(struct platform_device *pdev) -{ - void (*setup_pinmux)(void); - struct resource *res; - void __iomem *addr; - int status = 0, i; - - /* Platform data holds setup_pinmux function ptr */ - if (!pdev->dev.platform_data) - return -ENODEV; - - /* - * first try to register with vpfe. If not correct platform, then we - * don't have to iomap - */ - status = vpfe_register_ccdc_device(&isif_hw_dev); - if (status < 0) - return status; - - setup_pinmux = pdev->dev.platform_data; - /* - * setup Mux configuration for ccdc which may be different for - * different SoCs using this CCDC - */ - setup_pinmux(); - - i = 0; - /* Get the ISIF base address, linearization table0 and table1 addr. */ - while (i < 3) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) { - status = -ENODEV; - goto fail_nobase_res; - } - res = request_mem_region(res->start, resource_size(res), - res->name); - if (!res) { - status = -EBUSY; - goto fail_nobase_res; - } - addr = ioremap(res->start, resource_size(res)); - if (!addr) { - status = -ENOMEM; - goto fail_base_iomap; - } - switch (i) { - case 0: - /* ISIF base address */ - isif_cfg.base_addr = addr; - break; - case 1: - /* ISIF linear tbl0 address */ - isif_cfg.linear_tbl0_addr = addr; - break; - default: - /* ISIF linear tbl0 address */ - isif_cfg.linear_tbl1_addr = addr; - break; - } - i++; - } - isif_cfg.dev = &pdev->dev; - - printk(KERN_NOTICE "%s is registered with vpfe.\n", - isif_hw_dev.name); - return 0; -fail_base_iomap: - release_mem_region(res->start, resource_size(res)); - i--; -fail_nobase_res: - if (isif_cfg.base_addr) { - iounmap(isif_cfg.base_addr); - isif_cfg.base_addr = NULL; - } - if (isif_cfg.linear_tbl0_addr) { - iounmap(isif_cfg.linear_tbl0_addr); - isif_cfg.linear_tbl0_addr = NULL; - } - - while (i >= 0) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (res) - release_mem_region(res->start, resource_size(res)); - i--; - } - vpfe_unregister_ccdc_device(&isif_hw_dev); - return status; -} - -static int isif_remove(struct platform_device *pdev) -{ - struct resource *res; - int i = 0; - - iounmap(isif_cfg.base_addr); - isif_cfg.base_addr = NULL; - iounmap(isif_cfg.linear_tbl0_addr); - isif_cfg.linear_tbl0_addr = NULL; - iounmap(isif_cfg.linear_tbl1_addr); - isif_cfg.linear_tbl1_addr = NULL; - while (i < 3) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - release_mem_region(res->start, resource_size(res)); - i++; - } - vpfe_unregister_ccdc_device(&isif_hw_dev); - return 0; -} - -static struct platform_driver isif_driver = { - .driver = { - .name = "isif", - }, - .remove = isif_remove, - .probe = isif_probe, -}; - -module_platform_driver(isif_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/ti/davinci/isif_regs.h b/drivers/media/platform/ti/davinci/isif_regs.h deleted file mode 100644 index d68d38841ae7..000000000000 --- a/drivers/media/platform/ti/davinci/isif_regs.h +++ /dev/null @@ -1,256 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - */ -#ifndef _ISIF_REGS_H -#define _ISIF_REGS_H - -/* ISIF registers relative offsets */ -#define SYNCEN 0x00 -#define MODESET 0x04 -#define HDW 0x08 -#define VDW 0x0c -#define PPLN 0x10 -#define LPFR 0x14 -#define SPH 0x18 -#define LNH 0x1c -#define SLV0 0x20 -#define SLV1 0x24 -#define LNV 0x28 -#define CULH 0x2c -#define CULV 0x30 -#define HSIZE 0x34 -#define SDOFST 0x38 -#define CADU 0x3c -#define CADL 0x40 -#define LINCFG0 0x44 -#define LINCFG1 0x48 -#define CCOLP 0x4c -#define CRGAIN 0x50 -#define CGRGAIN 0x54 -#define CGBGAIN 0x58 -#define CBGAIN 0x5c -#define COFSTA 0x60 -#define FLSHCFG0 0x64 -#define FLSHCFG1 0x68 -#define FLSHCFG2 0x6c -#define VDINT0 0x70 -#define VDINT1 0x74 -#define VDINT2 0x78 -#define MISC 0x7c -#define CGAMMAWD 0x80 -#define REC656IF 0x84 -#define CCDCFG 0x88 -/***************************************************** -* Defect Correction registers -*****************************************************/ -#define DFCCTL 0x8c -#define VDFSATLV 0x90 -#define DFCMEMCTL 0x94 -#define DFCMEM0 0x98 -#define DFCMEM1 0x9c -#define DFCMEM2 0xa0 -#define DFCMEM3 0xa4 -#define DFCMEM4 0xa8 -/**************************************************** -* Black Clamp registers -****************************************************/ -#define CLAMPCFG 0xac -#define CLDCOFST 0xb0 -#define CLSV 0xb4 -#define CLHWIN0 0xb8 -#define CLHWIN1 0xbc -#define CLHWIN2 0xc0 -#define CLVRV 0xc4 -#define CLVWIN0 0xc8 -#define CLVWIN1 0xcc -#define CLVWIN2 0xd0 -#define CLVWIN3 0xd4 -/**************************************************** -* Lense Shading Correction -****************************************************/ -#define DATAHOFST 0xd8 -#define DATAVOFST 0xdc -#define LSCHVAL 0xe0 -#define LSCVVAL 0xe4 -#define TWODLSCCFG 0xe8 -#define TWODLSCOFST 0xec -#define TWODLSCINI 0xf0 -#define TWODLSCGRBU 0xf4 -#define TWODLSCGRBL 0xf8 -#define TWODLSCGROF 0xfc -#define TWODLSCORBU 0x100 -#define TWODLSCORBL 0x104 -#define TWODLSCOROF 0x108 -#define TWODLSCIRQEN 0x10c -#define TWODLSCIRQST 0x110 -/**************************************************** -* Data formatter -****************************************************/ -#define FMTCFG 0x114 -#define FMTPLEN 0x118 -#define FMTSPH 0x11c -#define FMTLNH 0x120 -#define FMTSLV 0x124 -#define FMTLNV 0x128 -#define FMTRLEN 0x12c -#define FMTHCNT 0x130 -#define FMTAPTR_BASE 0x134 -/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ -#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) -#define FMTPGMVF0 0x174 -#define FMTPGMVF1 0x178 -#define FMTPGMAPU0 0x17c -#define FMTPGMAPU1 0x180 -#define FMTPGMAPS0 0x184 -#define FMTPGMAPS1 0x188 -#define FMTPGMAPS2 0x18c -#define FMTPGMAPS3 0x190 -#define FMTPGMAPS4 0x194 -#define FMTPGMAPS5 0x198 -#define FMTPGMAPS6 0x19c -#define FMTPGMAPS7 0x1a0 -/************************************************ -* Color Space Converter -************************************************/ -#define CSCCTL 0x1a4 -#define CSCM0 0x1a8 -#define CSCM1 0x1ac -#define CSCM2 0x1b0 -#define CSCM3 0x1b4 -#define CSCM4 0x1b8 -#define CSCM5 0x1bc -#define CSCM6 0x1c0 -#define CSCM7 0x1c4 -#define OBWIN0 0x1c8 -#define OBWIN1 0x1cc -#define OBWIN2 0x1d0 -#define OBWIN3 0x1d4 -#define OBVAL0 0x1d8 -#define OBVAL1 0x1dc -#define OBVAL2 0x1e0 -#define OBVAL3 0x1e4 -#define OBVAL4 0x1e8 -#define OBVAL5 0x1ec -#define OBVAL6 0x1f0 -#define OBVAL7 0x1f4 -#define CLKCTL 0x1f8 - -/* Masks & Shifts below */ -#define START_PX_HOR_MASK 0x7FFF -#define NUM_PX_HOR_MASK 0x7FFF -#define START_VER_ONE_MASK 0x7FFF -#define START_VER_TWO_MASK 0x7FFF -#define NUM_LINES_VER 0x7FFF - -/* gain - offset masks */ -#define GAIN_INTEGER_SHIFT 9 -#define OFFSET_MASK 0xFFF -#define GAIN_SDRAM_EN_SHIFT 12 -#define GAIN_IPIPE_EN_SHIFT 13 -#define GAIN_H3A_EN_SHIFT 14 -#define OFST_SDRAM_EN_SHIFT 8 -#define OFST_IPIPE_EN_SHIFT 9 -#define OFST_H3A_EN_SHIFT 10 -#define GAIN_OFFSET_EN_MASK 0x7700 - -/* Culling */ -#define CULL_PAT_EVEN_LINE_SHIFT 8 - -/* CCDCFG register */ -#define ISIF_YCINSWP_RAW (0x00 << 4) -#define ISIF_YCINSWP_YCBCR (0x01 << 4) -#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) -#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) -#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) -#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) -#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) -#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) -#define ISIF_DATA_PACK_MASK 3 -#define ISIF_DATA_PACK16 0 -#define ISIF_DATA_PACK12 1 -#define ISIF_DATA_PACK8 2 -#define ISIF_PIX_ORDER_SHIFT 11 -#define ISIF_BW656_ENABLE (0x01 << 5) - -/* MODESET registers */ -#define ISIF_VDHDOUT_INPUT (0x00 << 0) -#define ISIF_INPUT_SHIFT 12 -#define ISIF_RAW_INPUT_MODE 0 -#define ISIF_FID_POL_SHIFT 4 -#define ISIF_HD_POL_SHIFT 3 -#define ISIF_VD_POL_SHIFT 2 -#define ISIF_DATAPOL_NORMAL 0 -#define ISIF_DATAPOL_SHIFT 6 -#define ISIF_EXWEN_DISABLE 0 -#define ISIF_EXWEN_SHIFT 5 -#define ISIF_FRM_FMT_SHIFT 7 -#define ISIF_DATASFT_SHIFT 8 -#define ISIF_LPF_SHIFT 14 -#define ISIF_LPF_MASK 1 - -/* GAMMAWD registers */ -#define ISIF_ALAW_GAMMA_WD_MASK 0xF -#define ISIF_ALAW_GAMMA_WD_SHIFT 1 -#define ISIF_ALAW_ENABLE 1 -#define ISIF_GAMMAWD_CFA_SHIFT 5 - -/* HSIZE registers */ -#define ISIF_HSIZE_FLIP_MASK 1 -#define ISIF_HSIZE_FLIP_SHIFT 12 - -/* MISC registers */ -#define ISIF_DPCM_EN_SHIFT 12 -#define ISIF_DPCM_PREDICTOR_SHIFT 13 - -/* Black clamp related */ -#define ISIF_BC_MODE_COLOR_SHIFT 4 -#define ISIF_HORZ_BC_MODE_SHIFT 1 -#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 -#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 -#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 -#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 -#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 -#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 - -/* VDFC registers */ -#define ISIF_VDFC_EN_SHIFT 4 -#define ISIF_VDFC_CORR_MOD_SHIFT 5 -#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 -#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 -#define ISIF_VDFC_POS_MASK 0x1FFF -#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 - -/* CSC registers */ -#define ISIF_CSC_COEF_INTEG_MASK 7 -#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f -#define ISIF_CSC_COEF_INTEG_SHIFT 5 -#define ISIF_CSCM_MSB_SHIFT 8 -#define ISIF_DF_CSC_SPH_MASK 0x1FFF -#define ISIF_DF_CSC_LNH_MASK 0x1FFF -#define ISIF_DF_CSC_SLV_MASK 0x1FFF -#define ISIF_DF_CSC_LNV_MASK 0x1FFF -#define ISIF_DF_NUMLINES 0x7FFF -#define ISIF_DF_NUMPIX 0x1FFF - -/* Offsets for LSC/DFC/Gain */ -#define ISIF_DATA_H_OFFSET_MASK 0x1FFF -#define ISIF_DATA_V_OFFSET_MASK 0x1FFF - -/* Linearization */ -#define ISIF_LIN_CORRSFT_SHIFT 4 -#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 - - -/* Pattern registers */ -#define ISIF_PG_EN (1 << 3) -#define ISIF_SEL_PG_SRC (3 << 4) -#define ISIF_PG_VD_POL_SHIFT 0 -#define ISIF_PG_HD_POL_SHIFT 1 - -/*random other junk*/ -#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) -#define ISIF_SYNCEN_WEN_MASK (1 << 1) -#define ISIF_SYNCEN_WEN_SHIFT 1 - -#endif diff --git a/drivers/media/platform/ti/davinci/vpfe_capture.c b/drivers/media/platform/ti/davinci/vpfe_capture.c deleted file mode 100644 index 0a2226b321d7..000000000000 --- a/drivers/media/platform/ti/davinci/vpfe_capture.c +++ /dev/null @@ -1,1902 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * Driver name : VPFE Capture driver - * VPFE Capture driver allows applications to capture and stream video - * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as - * TVP5146 or Raw Bayer RGB image data from an image sensor - * such as Microns' MT9T001, MT9T031 etc. - * - * These SoCs have, in common, a Video Processing Subsystem (VPSS) that - * consists of a Video Processing Front End (VPFE) for capturing - * video/raw image data and Video Processing Back End (VPBE) for displaying - * YUV data through an in-built analog encoder or Digital LCD port. This - * driver is for capture through VPFE. A typical EVM using these SoCs have - * following high level configuration. - * - * decoder(TVP5146/ YUV/ - * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) - * data input | | - * V | - * SDRAM | - * V - * Image Processor - * | - * V - * SDRAM - * The data flow happens from a decoder connected to the VPFE over a - * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface - * and to the input of VPFE through an optional MUX (if more inputs are - * to be interfaced on the EVM). The input data is first passed through - * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC - * does very little or no processing on YUV data and does pre-process Raw - * Bayer RGB data through modules such as Defect Pixel Correction (DFC) - * Color Space Conversion (CSC), data gain/offset etc. After this, data - * can be written to SDRAM or can be connected to the image processing - * block such as IPIPE (on DM355 only). - * - * Features supported - * - MMAP IO - * - Capture using TVP5146 over BT.656 - * - support for interfacing decoders using sub device model - * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV - * data capture to SDRAM. - * TODO list - * - Support multiple REQBUF after open - * - Support for de-allocating buffers through REQBUF - * - Support for Raw Bayer RGB capture - * - Support for chaining Image Processor - * - Support for static allocation of buffers - * - Support for USERPTR IO - * - Support for STREAMON before QBUF - * - Support for control ioctls - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "ccdc_hw_device.h" - -static int debug; -static u32 numbuffers = 3; -static u32 bufsize = (720 * 576 * 2); - -module_param(numbuffers, uint, S_IRUGO); -module_param(bufsize, uint, S_IRUGO); -module_param(debug, int, 0644); - -MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); -MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); -MODULE_PARM_DESC(debug, "Debug level 0-1"); - -MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Texas Instruments"); - -/* standard information */ -struct vpfe_standard { - v4l2_std_id std_id; - unsigned int width; - unsigned int height; - struct v4l2_fract pixelaspect; - /* 0 - progressive, 1 - interlaced */ - int frame_format; -}; - -/* ccdc configuration */ -struct ccdc_config { - /* This make sure vpfe is probed and ready to go */ - int vpfe_probed; - /* name of ccdc device */ - char name[32]; -}; - -/* data structures */ -static struct vpfe_config_params config_params = { - .min_numbuffers = 3, - .numbuffers = 3, - .min_bufsize = 720 * 480 * 2, - .device_bufsize = 720 * 576 * 2, -}; - -/* ccdc device registered */ -static const struct ccdc_hw_device *ccdc_dev; -/* lock for accessing ccdc information */ -static DEFINE_MUTEX(ccdc_lock); -/* ccdc configuration */ -static struct ccdc_config *ccdc_cfg; - -static const struct vpfe_standard vpfe_standards[] = { - {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, - {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, -}; - -/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ -static const struct vpfe_pixel_format vpfe_pix_fmts[] = { - { - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .bpp = 1, - }, - { - .pixelformat = V4L2_PIX_FMT_SBGGR16, - .bpp = 2, - }, - { - .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, - .bpp = 1, - }, - { - .pixelformat = V4L2_PIX_FMT_UYVY, - .bpp = 2, - }, - { - .pixelformat = V4L2_PIX_FMT_YUYV, - .bpp = 2, - }, - { - .pixelformat = V4L2_PIX_FMT_NV12, - .bpp = 1, - }, -}; - -/* - * vpfe_lookup_pix_format() - * lookup an entry in the vpfe pix format table based on pix_format - */ -static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { - if (pix_format == vpfe_pix_fmts[i].pixelformat) - return &vpfe_pix_fmts[i]; - } - return NULL; -} - -/* - * vpfe_register_ccdc_device. CCDC module calls this to - * register with vpfe capture - */ -int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev) -{ - int ret = 0; - printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); - - if (!dev->hw_ops.open || - !dev->hw_ops.enable || - !dev->hw_ops.set_hw_if_params || - !dev->hw_ops.configure || - !dev->hw_ops.set_buftype || - !dev->hw_ops.get_buftype || - !dev->hw_ops.enum_pix || - !dev->hw_ops.set_frame_format || - !dev->hw_ops.get_frame_format || - !dev->hw_ops.get_pixel_format || - !dev->hw_ops.set_pixel_format || - !dev->hw_ops.set_image_window || - !dev->hw_ops.get_image_window || - !dev->hw_ops.get_line_length || - !dev->hw_ops.getfid) - return -EINVAL; - - mutex_lock(&ccdc_lock); - if (!ccdc_cfg) { - /* - * TODO. Will this ever happen? if so, we need to fix it. - * Probably we need to add the request to a linked list and - * walk through it during vpfe probe - */ - printk(KERN_ERR "vpfe capture not initialized\n"); - ret = -EFAULT; - goto unlock; - } - - if (strcmp(dev->name, ccdc_cfg->name)) { - /* ignore this ccdc */ - ret = -EINVAL; - goto unlock; - } - - if (ccdc_dev) { - printk(KERN_ERR "ccdc already registered\n"); - ret = -EINVAL; - goto unlock; - } - - ccdc_dev = dev; -unlock: - mutex_unlock(&ccdc_lock); - return ret; -} -EXPORT_SYMBOL(vpfe_register_ccdc_device); - -/* - * vpfe_unregister_ccdc_device. CCDC module calls this to - * unregister with vpfe capture - */ -void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev) -{ - if (!dev) { - printk(KERN_ERR "invalid ccdc device ptr\n"); - return; - } - - printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", - dev->name); - - if (strcmp(dev->name, ccdc_cfg->name)) { - /* ignore this ccdc */ - return; - } - - mutex_lock(&ccdc_lock); - ccdc_dev = NULL; - mutex_unlock(&ccdc_lock); -} -EXPORT_SYMBOL(vpfe_unregister_ccdc_device); - -/* - * vpfe_config_ccdc_image_format() - * For a pix format, configure ccdc to setup the capture - */ -static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) -{ - enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; - int ret = 0; - - if (ccdc_dev->hw_ops.set_pixel_format( - vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, - "couldn't set pix format in ccdc\n"); - return -EINVAL; - } - /* configure the image window */ - ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); - - switch (vpfe_dev->fmt.fmt.pix.field) { - case V4L2_FIELD_INTERLACED: - /* do nothing, since it is default */ - ret = ccdc_dev->hw_ops.set_buftype( - CCDC_BUFTYPE_FLD_INTERLEAVED); - break; - case V4L2_FIELD_NONE: - frm_fmt = CCDC_FRMFMT_PROGRESSIVE; - /* buffer type only applicable for interlaced scan */ - break; - case V4L2_FIELD_SEQ_TB: - ret = ccdc_dev->hw_ops.set_buftype( - CCDC_BUFTYPE_FLD_SEPARATED); - break; - default: - return -EINVAL; - } - - /* set the frame format */ - if (!ret) - ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); - return ret; -} -/* - * vpfe_config_image_format() - * For a given standard, this functions sets up the default - * pix format & crop values in the vpfe device and ccdc. It first - * starts with defaults based values from the standard table. - * It then checks if sub device supports get_fmt and then override the - * values based on that.Sets crop values to match with scan resolution - * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the - * values in ccdc - */ -static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, - v4l2_std_id std_id) -{ - struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; - struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { - if (vpfe_standards[i].std_id & std_id) { - vpfe_dev->std_info.active_pixels = - vpfe_standards[i].width; - vpfe_dev->std_info.active_lines = - vpfe_standards[i].height; - vpfe_dev->std_info.frame_format = - vpfe_standards[i].frame_format; - vpfe_dev->std_index = i; - break; - } - } - - if (i == ARRAY_SIZE(vpfe_standards)) { - v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); - return -EINVAL; - } - - vpfe_dev->crop.top = 0; - vpfe_dev->crop.left = 0; - vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; - vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; - pix->width = vpfe_dev->crop.width; - pix->height = vpfe_dev->crop.height; - - /* first field and frame format based on standard frame format */ - if (vpfe_dev->std_info.frame_format) { - pix->field = V4L2_FIELD_INTERLACED; - /* assume V4L2_PIX_FMT_UYVY as default */ - pix->pixelformat = V4L2_PIX_FMT_UYVY; - v4l2_fill_mbus_format(mbus_fmt, pix, - MEDIA_BUS_FMT_YUYV10_2X10); - } else { - pix->field = V4L2_FIELD_NONE; - /* assume V4L2_PIX_FMT_SBGGR8 */ - pix->pixelformat = V4L2_PIX_FMT_SBGGR8; - v4l2_fill_mbus_format(mbus_fmt, pix, - MEDIA_BUS_FMT_SBGGR8_1X8); - } - - /* if sub device supports get_fmt, override the defaults */ - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, - sdinfo->grp_id, pad, get_fmt, NULL, &fmt); - - if (ret && ret != -ENOIOCTLCMD) { - v4l2_err(&vpfe_dev->v4l2_dev, - "error in getting get_fmt from sub device\n"); - return ret; - } - v4l2_fill_pix_format(pix, mbus_fmt); - pix->bytesperline = pix->width * 2; - pix->sizeimage = pix->bytesperline * pix->height; - - /* Sets the values in CCDC */ - ret = vpfe_config_ccdc_image_format(vpfe_dev); - if (ret) - return ret; - - /* Update the values of sizeimage and bytesperline */ - pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); - pix->sizeimage = pix->bytesperline * pix->height; - - return 0; -} - -static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) -{ - int ret; - - /* set first input of current subdevice as the current input */ - vpfe_dev->current_input = 0; - - /* set default standard */ - vpfe_dev->std_index = 0; - - /* Configure the default format information */ - ret = vpfe_config_image_format(vpfe_dev, - vpfe_standards[vpfe_dev->std_index].std_id); - if (ret) - return ret; - - /* now open the ccdc device to initialize it */ - mutex_lock(&ccdc_lock); - if (!ccdc_dev) { - v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); - ret = -ENODEV; - goto unlock; - } - - if (!try_module_get(ccdc_dev->owner)) { - v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); - ret = -ENODEV; - goto unlock; - } - ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); - if (!ret) - vpfe_dev->initialized = 1; - - /* Clear all VPFE/CCDC interrupts */ - if (vpfe_dev->cfg->clr_intr) - vpfe_dev->cfg->clr_intr(-1); - -unlock: - mutex_unlock(&ccdc_lock); - return ret; -} - -/* - * vpfe_open : It creates object of file handle structure and - * stores it in private_data member of filepointer - */ -static int vpfe_open(struct file *file) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct video_device *vdev = video_devdata(file); - struct vpfe_fh *fh; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); - - if (!vpfe_dev->cfg->num_subdevs) { - v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); - return -ENODEV; - } - - /* Allocate memory for the file handle object */ - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - - /* store pointer to fh in private_data member of file */ - file->private_data = fh; - fh->vpfe_dev = vpfe_dev; - v4l2_fh_init(&fh->fh, vdev); - mutex_lock(&vpfe_dev->lock); - /* If decoder is not initialized. initialize it */ - if (!vpfe_dev->initialized) { - if (vpfe_initialize_device(vpfe_dev)) { - mutex_unlock(&vpfe_dev->lock); - v4l2_fh_exit(&fh->fh); - kfree(fh); - return -ENODEV; - } - } - /* Increment device usrs counter */ - vpfe_dev->usrs++; - /* Set io_allowed member to false */ - fh->io_allowed = 0; - v4l2_fh_add(&fh->fh); - mutex_unlock(&vpfe_dev->lock); - return 0; -} - -static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) -{ - unsigned long addr; - - vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, - struct videobuf_buffer, queue); - list_del(&vpfe_dev->next_frm->queue); - vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; - addr = videobuf_to_dma_contig(vpfe_dev->next_frm); - - ccdc_dev->hw_ops.setfbaddr(addr); -} - -static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) -{ - unsigned long addr; - - addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); - addr += vpfe_dev->field_off; - ccdc_dev->hw_ops.setfbaddr(addr); -} - -static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) -{ - 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); - vpfe_dev->cur_frm = vpfe_dev->next_frm; -} - -/* ISR for VINT0*/ -static irqreturn_t vpfe_isr(int irq, void *dev_id) -{ - struct vpfe_device *vpfe_dev = dev_id; - enum v4l2_field field; - int fid; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); - field = vpfe_dev->fmt.fmt.pix.field; - - /* if streaming not started, don't do anything */ - if (!vpfe_dev->started) - goto clear_intr; - - /* only for 6446 this will be applicable */ - if (ccdc_dev->hw_ops.reset) - ccdc_dev->hw_ops.reset(); - - if (field == V4L2_FIELD_NONE) { - /* handle progressive frame capture */ - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "frame format is progressive...\n"); - if (vpfe_dev->cur_frm != vpfe_dev->next_frm) - vpfe_process_buffer_complete(vpfe_dev); - goto clear_intr; - } - - /* interlaced or TB capture check which field we are in hardware */ - fid = ccdc_dev->hw_ops.getfid(); - - /* switch the software maintained field id */ - vpfe_dev->field_id ^= 1; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", - fid, vpfe_dev->field_id); - if (fid == vpfe_dev->field_id) { - /* we are in-sync here,continue */ - if (fid == 0) { - /* - * One frame is just being captured. If the next frame - * is available, release the current frame and move on - */ - if (vpfe_dev->cur_frm != vpfe_dev->next_frm) - vpfe_process_buffer_complete(vpfe_dev); - /* - * based on whether the two fields are stored - * interleavely or separately in memory, reconfigure - * the CCDC memory address - */ - if (field == V4L2_FIELD_SEQ_TB) - vpfe_schedule_bottom_field(vpfe_dev); - goto clear_intr; - } - /* - * if one field is just being captured configure - * the next frame get the next frame from the empty - * queue if no frame is available hold on to the - * current buffer - */ - spin_lock(&vpfe_dev->dma_queue_lock); - if (!list_empty(&vpfe_dev->dma_queue) && - vpfe_dev->cur_frm == vpfe_dev->next_frm) - vpfe_schedule_next_buffer(vpfe_dev); - spin_unlock(&vpfe_dev->dma_queue_lock); - } else if (fid == 0) { - /* - * out of sync. Recover from any hardware out-of-sync. - * May loose one frame - */ - vpfe_dev->field_id = fid; - } -clear_intr: - if (vpfe_dev->cfg->clr_intr) - vpfe_dev->cfg->clr_intr(irq); - - return IRQ_HANDLED; -} - -/* vdint1_isr - isr handler for VINT1 interrupt */ -static irqreturn_t vdint1_isr(int irq, void *dev_id) -{ - struct vpfe_device *vpfe_dev = dev_id; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); - - /* if streaming not started, don't do anything */ - if (!vpfe_dev->started) { - if (vpfe_dev->cfg->clr_intr) - vpfe_dev->cfg->clr_intr(irq); - return IRQ_HANDLED; - } - - spin_lock(&vpfe_dev->dma_queue_lock); - if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && - !list_empty(&vpfe_dev->dma_queue) && - vpfe_dev->cur_frm == vpfe_dev->next_frm) - vpfe_schedule_next_buffer(vpfe_dev); - spin_unlock(&vpfe_dev->dma_queue_lock); - - if (vpfe_dev->cfg->clr_intr) - vpfe_dev->cfg->clr_intr(irq); - - return IRQ_HANDLED; -} - -static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) -{ - enum ccdc_frmfmt frame_format; - - frame_format = ccdc_dev->hw_ops.get_frame_format(); - if (frame_format == CCDC_FRMFMT_PROGRESSIVE) - free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); -} - -static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) -{ - enum ccdc_frmfmt frame_format; - - frame_format = ccdc_dev->hw_ops.get_frame_format(); - if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { - return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, - 0, "vpfe_capture1", - vpfe_dev); - } - return 0; -} - -/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ -static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) -{ - vpfe_dev->started = 0; - ccdc_dev->hw_ops.enable(0); - if (ccdc_dev->hw_ops.enable_out_to_sdram) - ccdc_dev->hw_ops.enable_out_to_sdram(0); -} - -/* - * vpfe_release : This function deletes buffer queue, frees the - * buffers and the vpfe file handle - */ -static int vpfe_release(struct file *file) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_fh *fh = file->private_data; - struct vpfe_subdev_info *sdinfo; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); - - /* Get the device lock */ - mutex_lock(&vpfe_dev->lock); - /* if this instance is doing IO */ - if (fh->io_allowed) { - if (vpfe_dev->started) { - sdinfo = vpfe_dev->current_subdev; - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, - sdinfo->grp_id, - video, s_stream, 0); - if (ret && (ret != -ENOIOCTLCMD)) - v4l2_err(&vpfe_dev->v4l2_dev, - "stream off failed in subdev\n"); - vpfe_stop_ccdc_capture(vpfe_dev); - vpfe_detach_irq(vpfe_dev); - videobuf_streamoff(&vpfe_dev->buffer_queue); - } - vpfe_dev->io_usrs = 0; - vpfe_dev->numbuffers = config_params.numbuffers; - videobuf_stop(&vpfe_dev->buffer_queue); - videobuf_mmap_free(&vpfe_dev->buffer_queue); - } - - /* Decrement device usrs counter */ - vpfe_dev->usrs--; - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - /* If this is the last file handle */ - if (!vpfe_dev->usrs) { - vpfe_dev->initialized = 0; - if (ccdc_dev->hw_ops.close) - ccdc_dev->hw_ops.close(vpfe_dev->pdev); - module_put(ccdc_dev->owner); - } - mutex_unlock(&vpfe_dev->lock); - file->private_data = NULL; - /* Free memory allocated to file handle object */ - kfree(fh); - return 0; -} - -/* - * vpfe_mmap : It is used to map kernel space buffers - * into user spaces - */ -static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) -{ - /* Get the device object and file handle object */ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); - - return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); -} - -/* - * vpfe_poll: It is used for select/poll system call - */ -static __poll_t vpfe_poll(struct file *file, poll_table *wait) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); - - if (vpfe_dev->started) - return videobuf_poll_stream(file, - &vpfe_dev->buffer_queue, wait); - return 0; -} - -/* vpfe capture driver file operations */ -static const struct v4l2_file_operations vpfe_fops = { - .owner = THIS_MODULE, - .open = vpfe_open, - .release = vpfe_release, - .unlocked_ioctl = video_ioctl2, - .mmap = vpfe_mmap, - .poll = vpfe_poll -}; - -/* - * vpfe_check_format() - * This function adjust the input pixel format as per hardware - * capabilities and update the same in pixfmt. - * Following algorithm used :- - * - * If given pixformat is not in the vpfe list of pix formats or not - * supported by the hardware, current value of pixformat in the device - * is used - * If given field is not supported, then current field is used. If field - * is different from current, then it is matched with that from sub device. - * Minimum height is 2 lines for interlaced or tb field and 1 line for - * progressive. Maximum height is clamped to active active lines of scan - * Minimum width is 32 bytes in memory and width is clamped to active - * pixels of scan. - * bytesperline is a multiple of 32. - */ -static const struct vpfe_pixel_format * - vpfe_check_format(struct vpfe_device *vpfe_dev, - struct v4l2_pix_format *pixfmt) -{ - u32 min_height = 1, min_width = 32, max_width, max_height; - const struct vpfe_pixel_format *vpfe_pix_fmt; - u32 pix; - int temp, found; - - vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); - if (!vpfe_pix_fmt) { - /* - * use current pixel format in the vpfe device. We - * will find this pix format in the table - */ - pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; - vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); - } - - /* check if hw supports it */ - temp = 0; - found = 0; - while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { - if (vpfe_pix_fmt->pixelformat == pix) { - found = 1; - break; - } - temp++; - } - - if (!found) { - /* use current pixel format */ - pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; - /* - * Since this is currently used in the vpfe device, we - * will find this pix format in the table - */ - vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); - } - - /* check what field format is supported */ - if (pixfmt->field == V4L2_FIELD_ANY) { - /* if field is any, use current value as default */ - pixfmt->field = vpfe_dev->fmt.fmt.pix.field; - } - - /* - * if field is not same as current field in the vpfe device - * try matching the field with the sub device field - */ - if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { - /* - * If field value is not in the supported fields, use current - * field used in the device as default - */ - switch (pixfmt->field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - /* if sub device is supporting progressive, use that */ - if (!vpfe_dev->std_info.frame_format) - pixfmt->field = V4L2_FIELD_NONE; - break; - case V4L2_FIELD_NONE: - if (vpfe_dev->std_info.frame_format) - pixfmt->field = V4L2_FIELD_INTERLACED; - break; - - default: - /* use current field as default */ - pixfmt->field = vpfe_dev->fmt.fmt.pix.field; - break; - } - } - - /* Now adjust image resolutions supported */ - if (pixfmt->field == V4L2_FIELD_INTERLACED || - pixfmt->field == V4L2_FIELD_SEQ_TB) - min_height = 2; - - max_width = vpfe_dev->std_info.active_pixels; - max_height = vpfe_dev->std_info.active_lines; - min_width /= vpfe_pix_fmt->bpp; - - v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", - pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); - - pixfmt->width = clamp((pixfmt->width), min_width, max_width); - pixfmt->height = clamp((pixfmt->height), min_height, max_height); - - /* If interlaced, adjust height to be a multiple of 2 */ - if (pixfmt->field == V4L2_FIELD_INTERLACED) - pixfmt->height &= (~1); - /* - * recalculate bytesperline and sizeimage since width - * and height might have changed - */ - pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) - & ~31); - if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) - pixfmt->sizeimage = - pixfmt->bytesperline * pixfmt->height + - ((pixfmt->bytesperline * pixfmt->height) >> 1); - else - pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; - - v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height = %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", - pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, - pixfmt->bytesperline, pixfmt->sizeimage); - return vpfe_pix_fmt; -} - -static int vpfe_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); - - strscpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); - strscpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); - strscpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); - return 0; -} - -static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); - /* Fill in the information about format */ - *fmt = vpfe_dev->fmt; - return 0; -} - -static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - const struct vpfe_pixel_format *pix_fmt; - u32 pix; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); - - if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) - return -EINVAL; - - /* Fill in the information about format */ - pix_fmt = vpfe_lookup_pix_format(pix); - if (pix_fmt) { - fmt->pixelformat = pix_fmt->pixelformat; - return 0; - } - return -EINVAL; -} - -static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - const struct vpfe_pixel_format *pix_fmts; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); - - /* If streaming is started, return error */ - if (vpfe_dev->started) { - v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); - return -EBUSY; - } - - /* Check for valid frame format */ - pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); - if (!pix_fmts) - return -EINVAL; - - /* store the pixel format in the device object */ - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - /* First detach any IRQ if currently attached */ - vpfe_detach_irq(vpfe_dev); - vpfe_dev->fmt = *fmt; - /* set image capture parameters in the ccdc */ - ret = vpfe_config_ccdc_image_format(vpfe_dev); - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - const struct vpfe_pixel_format *pix_fmts; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); - - pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); - if (!pix_fmts) - return -EINVAL; - return 0; -} - -/* - * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a - * given app input index - */ -static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, - int *subdev_index, - int *subdev_input_index, - int app_input_index) -{ - struct vpfe_config *cfg = vpfe_dev->cfg; - struct vpfe_subdev_info *sdinfo; - int i, j = 0; - - for (i = 0; i < cfg->num_subdevs; i++) { - sdinfo = &cfg->sub_devs[i]; - if (app_input_index < (j + sdinfo->num_inputs)) { - *subdev_index = i; - *subdev_input_index = app_input_index - j; - return 0; - } - j += sdinfo->num_inputs; - } - return -EINVAL; -} - -/* - * vpfe_get_app_input - Get app input index for a given subdev input index - * driver stores the input index of the current sub device and translate it - * when application request the current input - */ -static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, - int *app_input_index) -{ - struct vpfe_config *cfg = vpfe_dev->cfg; - struct vpfe_subdev_info *sdinfo; - int i, j = 0; - - for (i = 0; i < cfg->num_subdevs; i++) { - sdinfo = &cfg->sub_devs[i]; - if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { - if (vpfe_dev->current_input >= sdinfo->num_inputs) - return -1; - *app_input_index = j + vpfe_dev->current_input; - return 0; - } - j += sdinfo->num_inputs; - } - return -EINVAL; -} - -static int vpfe_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - int subdev, index ; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); - - if (vpfe_get_subdev_input_index(vpfe_dev, - &subdev, - &index, - inp->index) < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, "input information not found for the subdev\n"); - return -EINVAL; - } - sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; - *inp = sdinfo->inputs[index]; - return 0; -} - -static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); - - return vpfe_get_app_input_index(vpfe_dev, index); -} - - -static int vpfe_s_input(struct file *file, void *priv, unsigned int index) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct v4l2_subdev *sd; - struct vpfe_subdev_info *sdinfo; - int subdev_index, inp_index; - struct vpfe_route *route; - u32 input, output; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - /* - * If streaming is started return device busy - * error - */ - if (vpfe_dev->started) { - v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); - ret = -EBUSY; - goto unlock_out; - } - ret = vpfe_get_subdev_input_index(vpfe_dev, - &subdev_index, - &inp_index, - index); - if (ret < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); - goto unlock_out; - } - - sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; - sd = vpfe_dev->sd[subdev_index]; - route = &sdinfo->routes[inp_index]; - if (route && sdinfo->can_route) { - input = route->input; - output = route->output; - } else { - input = 0; - output = 0; - } - - if (sd) - ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); - - if (ret) { - v4l2_err(&vpfe_dev->v4l2_dev, - "vpfe_doioctl:error in setting input in decoder\n"); - ret = -EINVAL; - goto unlock_out; - } - vpfe_dev->current_subdev = sdinfo; - if (sd) - vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler; - vpfe_dev->current_input = index; - vpfe_dev->std_index = 0; - - /* set the bus/interface parameter for the sub device in ccdc */ - ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); - if (ret) - goto unlock_out; - - /* set the default image parameters in the device */ - ret = vpfe_config_image_format(vpfe_dev, - vpfe_standards[vpfe_dev->std_index].std_id); -unlock_out: - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - sdinfo = vpfe_dev->current_subdev; - if (ret) - return ret; - /* Call querystd function of decoder device */ - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, querystd, std_id); - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_subdev_info *sdinfo; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); - - /* Call decoder driver function to set the standard */ - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - sdinfo = vpfe_dev->current_subdev; - /* If streaming is started, return device busy error */ - if (vpfe_dev->started) { - v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); - ret = -EBUSY; - goto unlock_out; - } - - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, s_std, std_id); - if (ret < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); - goto unlock_out; - } - ret = vpfe_config_image_format(vpfe_dev, std_id); - -unlock_out: - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); - - *std_id = vpfe_standards[vpfe_dev->std_index].std_id; - return 0; -} -/* - * Videobuf operations - */ -static int vpfe_videobuf_setup(struct videobuf_queue *vq, - unsigned int *count, - unsigned int *size) -{ - struct vpfe_fh *fh = vq->priv_data; - struct vpfe_device *vpfe_dev = fh->vpfe_dev; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); - *size = vpfe_dev->fmt.fmt.pix.sizeimage; - if (vpfe_dev->memory == V4L2_MEMORY_MMAP && - vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) - *size = config_params.device_bufsize; - - if (*count < config_params.min_numbuffers) - *count = config_params.min_numbuffers; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "count=%d, size=%d\n", *count, *size); - return 0; -} - -static int vpfe_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct vpfe_fh *fh = vq->priv_data; - struct vpfe_device *vpfe_dev = fh->vpfe_dev; - unsigned long addr; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); - - /* If buffer is not initialized, initialize it */ - if (VIDEOBUF_NEEDS_INIT == vb->state) { - vb->width = vpfe_dev->fmt.fmt.pix.width; - vb->height = vpfe_dev->fmt.fmt.pix.height; - vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; - vb->field = field; - - ret = videobuf_iolock(vq, vb, NULL); - if (ret < 0) - return ret; - - addr = videobuf_to_dma_contig(vb); - /* Make sure user addresses are aligned to 32 bytes */ - if (!ALIGN(addr, 32)) - return -EINVAL; - - vb->state = VIDEOBUF_PREPARED; - } - return 0; -} - -static void vpfe_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - /* Get the file handle object and device object */ - struct vpfe_fh *fh = vq->priv_data; - struct vpfe_device *vpfe_dev = fh->vpfe_dev; - unsigned long flags; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); - - /* add the buffer to the DMA queue */ - spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); - list_add_tail(&vb->queue, &vpfe_dev->dma_queue); - spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); - - /* Change state of the buffer */ - vb->state = VIDEOBUF_QUEUED; -} - -static void vpfe_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct vpfe_fh *fh = vq->priv_data; - struct vpfe_device *vpfe_dev = fh->vpfe_dev; - unsigned long flags; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); - - /* - * We need to flush the buffer from the dma queue since - * they are de-allocated - */ - spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); - INIT_LIST_HEAD(&vpfe_dev->dma_queue); - spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static const struct videobuf_queue_ops vpfe_videobuf_qops = { - .buf_setup = vpfe_videobuf_setup, - .buf_prepare = vpfe_videobuf_prepare, - .buf_queue = vpfe_videobuf_queue, - .buf_release = vpfe_videobuf_release, -}; - -/* - * vpfe_reqbufs. currently support REQBUF only once opening - * the device. - */ -static int vpfe_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req_buf) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_fh *fh = file->private_data; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); - return -EINVAL; - } - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - if (vpfe_dev->io_usrs != 0) { - v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); - ret = -EBUSY; - goto unlock_out; - } - - vpfe_dev->memory = req_buf->memory; - videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, - &vpfe_videobuf_qops, - vpfe_dev->pdev, - &vpfe_dev->irqlock, - req_buf->type, - vpfe_dev->fmt.fmt.pix.field, - sizeof(struct videobuf_buffer), - fh, NULL); - - fh->io_allowed = 1; - vpfe_dev->io_usrs = 1; - INIT_LIST_HEAD(&vpfe_dev->dma_queue); - ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); -unlock_out: - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); - return -EINVAL; - } - - if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); - return -EINVAL; - } - /* Call videobuf_querybuf to get information */ - return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); -} - -static int vpfe_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_fh *fh = file->private_data; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); - return -EINVAL; - } - - /* - * If this file handle is not allowed to do IO, - * return error - */ - if (!fh->io_allowed) { - v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); - return -EACCES; - } - return videobuf_qbuf(&vpfe_dev->buffer_queue, p); -} - -static int vpfe_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); - return -EINVAL; - } - return videobuf_dqbuf(&vpfe_dev->buffer_queue, - buf, file->f_flags & O_NONBLOCK); -} - -/* - * vpfe_calculate_offsets : This function calculates buffers offset - * for top and bottom field - */ -static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) -{ - struct v4l2_rect image_win; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); - - ccdc_dev->hw_ops.get_image_window(&image_win); - vpfe_dev->field_off = image_win.height * image_win.width; -} - -/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ -static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) -{ - ccdc_dev->hw_ops.enable(1); - if (ccdc_dev->hw_ops.enable_out_to_sdram) - ccdc_dev->hw_ops.enable_out_to_sdram(1); - vpfe_dev->started = 1; -} - -/* - * vpfe_streamon. Assume the DMA queue is not empty. - * application is expected to call QBUF before calling - * this ioctl. If not, driver returns error - */ -static int vpfe_streamon(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_fh *fh = file->private_data; - struct vpfe_subdev_info *sdinfo; - unsigned long addr; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); - return -EINVAL; - } - - /* If file handle is not allowed IO, return error */ - if (!fh->io_allowed) { - v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); - return -EACCES; - } - - sdinfo = vpfe_dev->current_subdev; - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, s_stream, 1); - - if (ret && (ret != -ENOIOCTLCMD)) { - v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); - return -EINVAL; - } - - /* If buffer queue is empty, return error */ - if (list_empty(&vpfe_dev->buffer_queue.stream)) { - v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); - return -EIO; - } - - /* Call videobuf_streamon to start streaming * in videobuf */ - ret = videobuf_streamon(&vpfe_dev->buffer_queue); - if (ret) - return ret; - - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - goto streamoff; - /* Get the next frame from the buffer queue */ - vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, - struct videobuf_buffer, queue); - vpfe_dev->cur_frm = vpfe_dev->next_frm; - /* Remove buffer from the buffer queue */ - list_del(&vpfe_dev->cur_frm->queue); - /* Mark state of the current frame to active */ - vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; - /* Initialize field_id and started member */ - vpfe_dev->field_id = 0; - addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); - - /* Calculate field offset */ - vpfe_calculate_offsets(vpfe_dev); - - if (vpfe_attach_irq(vpfe_dev) < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, - "Error in attaching interrupt handle\n"); - ret = -EFAULT; - goto unlock_out; - } - if (ccdc_dev->hw_ops.configure() < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, - "Error in configuring ccdc\n"); - ret = -EINVAL; - goto unlock_out; - } - ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); - vpfe_start_ccdc_capture(vpfe_dev); - mutex_unlock(&vpfe_dev->lock); - return ret; -unlock_out: - mutex_unlock(&vpfe_dev->lock); -streamoff: - videobuf_streamoff(&vpfe_dev->buffer_queue); - return ret; -} - -static int vpfe_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct vpfe_fh *fh = file->private_data; - struct vpfe_subdev_info *sdinfo; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); - return -EINVAL; - } - - /* If io is allowed for this file handle, return error */ - if (!fh->io_allowed) { - v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); - return -EACCES; - } - - /* If streaming is not started, return error */ - if (!vpfe_dev->started) { - v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); - return -EINVAL; - } - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - vpfe_stop_ccdc_capture(vpfe_dev); - vpfe_detach_irq(vpfe_dev); - - sdinfo = vpfe_dev->current_subdev; - ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - video, s_stream, 0); - - if (ret && (ret != -ENOIOCTLCMD)) - v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); - ret = videobuf_streamoff(&vpfe_dev->buffer_queue); - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -static int vpfe_g_pixelaspect(struct file *file, void *priv, - int type, struct v4l2_fract *f) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_pixelaspect\n"); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - /* If std_index is invalid, then just return (== 1:1 aspect) */ - if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) - return 0; - - *f = vpfe_standards[vpfe_dev->std_index].pixelaspect; - return 0; -} - -static int vpfe_g_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_selection\n"); - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = vpfe_dev->crop; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.width = vpfe_standards[vpfe_dev->std_index].width; - sel->r.height = vpfe_standards[vpfe_dev->std_index].height; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vpfe_s_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - struct v4l2_rect rect = sel->r; - int ret; - - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_selection\n"); - - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (vpfe_dev->started) { - /* make sure streaming is not started */ - v4l2_err(&vpfe_dev->v4l2_dev, - "Cannot change crop when streaming is ON\n"); - return -EBUSY; - } - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - if (rect.top < 0 || rect.left < 0) { - v4l2_err(&vpfe_dev->v4l2_dev, - "doesn't support negative values for top & left\n"); - ret = -EINVAL; - goto unlock_out; - } - - /* adjust the width to 16 pixel boundary */ - rect.width = ((rect.width + 15) & ~0xf); - - /* make sure parameters are valid */ - if ((rect.left + rect.width > - vpfe_dev->std_info.active_pixels) || - (rect.top + rect.height > - vpfe_dev->std_info.active_lines)) { - v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_SELECTION params\n"); - ret = -EINVAL; - goto unlock_out; - } - ccdc_dev->hw_ops.set_image_window(&rect); - vpfe_dev->fmt.fmt.pix.width = rect.width; - vpfe_dev->fmt.fmt.pix.height = rect.height; - vpfe_dev->fmt.fmt.pix.bytesperline = - ccdc_dev->hw_ops.get_line_length(); - vpfe_dev->fmt.fmt.pix.sizeimage = - vpfe_dev->fmt.fmt.pix.bytesperline * - vpfe_dev->fmt.fmt.pix.height; - vpfe_dev->crop = rect; - sel->r = rect; -unlock_out: - mutex_unlock(&vpfe_dev->lock); - return ret; -} - -/* vpfe capture ioctl operations */ -static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { - .vidioc_querycap = vpfe_querycap, - .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, - .vidioc_enum_input = vpfe_enum_input, - .vidioc_g_input = vpfe_g_input, - .vidioc_s_input = vpfe_s_input, - .vidioc_querystd = vpfe_querystd, - .vidioc_s_std = vpfe_s_std, - .vidioc_g_std = vpfe_g_std, - .vidioc_reqbufs = vpfe_reqbufs, - .vidioc_querybuf = vpfe_querybuf, - .vidioc_qbuf = vpfe_qbuf, - .vidioc_dqbuf = vpfe_dqbuf, - .vidioc_streamon = vpfe_streamon, - .vidioc_streamoff = vpfe_streamoff, - .vidioc_g_pixelaspect = vpfe_g_pixelaspect, - .vidioc_g_selection = vpfe_g_selection, - .vidioc_s_selection = vpfe_s_selection, -}; - -static struct vpfe_device *vpfe_initialize(void) -{ - struct vpfe_device *vpfe_dev; - - /* Default number of buffers should be 3 */ - if ((numbuffers > 0) && - (numbuffers < config_params.min_numbuffers)) - numbuffers = config_params.min_numbuffers; - - /* - * Set buffer size to min buffers size if invalid buffer size is - * given - */ - if (bufsize < config_params.min_bufsize) - bufsize = config_params.min_bufsize; - - config_params.numbuffers = numbuffers; - - if (numbuffers) - config_params.device_bufsize = bufsize; - - /* Allocate memory for device objects */ - vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); - - return vpfe_dev; -} - -/* - * vpfe_probe : This function creates device entries by register - * itself to the V4L2 driver and initializes fields of each - * device objects - */ -static int vpfe_probe(struct platform_device *pdev) -{ - struct vpfe_subdev_info *sdinfo; - struct vpfe_config *vpfe_cfg; - struct resource *res1; - struct vpfe_device *vpfe_dev; - struct i2c_adapter *i2c_adap; - struct video_device *vfd; - int ret, i, j; - int num_subdevs = 0; - - /* Get the pointer to the device object */ - vpfe_dev = vpfe_initialize(); - - if (!vpfe_dev) { - v4l2_err(pdev->dev.driver, - "Failed to allocate memory for vpfe_dev\n"); - return -ENOMEM; - } - - vpfe_dev->pdev = &pdev->dev; - - if (!pdev->dev.platform_data) { - v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); - ret = -ENODEV; - goto probe_free_dev_mem; - } - - vpfe_cfg = pdev->dev.platform_data; - vpfe_dev->cfg = vpfe_cfg; - if (!vpfe_cfg->ccdc || !vpfe_cfg->card_name || !vpfe_cfg->sub_devs) { - v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); - ret = -ENOENT; - goto probe_free_dev_mem; - } - - /* Allocate memory for ccdc configuration */ - ccdc_cfg = kmalloc(sizeof(*ccdc_cfg), GFP_KERNEL); - if (!ccdc_cfg) { - ret = -ENOMEM; - goto probe_free_dev_mem; - } - - mutex_lock(&ccdc_lock); - - strscpy(ccdc_cfg->name, vpfe_cfg->ccdc, sizeof(ccdc_cfg->name)); - /* Get VINT0 irq resource */ - res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res1) { - v4l2_err(pdev->dev.driver, - "Unable to get interrupt for VINT0\n"); - ret = -ENODEV; - goto probe_free_ccdc_cfg_mem; - } - vpfe_dev->ccdc_irq0 = res1->start; - - /* Get VINT1 irq resource */ - res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!res1) { - v4l2_err(pdev->dev.driver, - "Unable to get interrupt for VINT1\n"); - ret = -ENODEV; - goto probe_free_ccdc_cfg_mem; - } - vpfe_dev->ccdc_irq1 = res1->start; - - ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0, - "vpfe_capture0", vpfe_dev); - - if (0 != ret) { - v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); - goto probe_free_ccdc_cfg_mem; - } - - vfd = &vpfe_dev->video_dev; - /* Initialize field of video device */ - vfd->release = video_device_release_empty; - vfd->fops = &vpfe_fops; - vfd->ioctl_ops = &vpfe_ioctl_ops; - vfd->tvnorms = 0; - vfd->v4l2_dev = &vpfe_dev->v4l2_dev; - vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - snprintf(vfd->name, sizeof(vfd->name), - "%s_V%d.%d.%d", - CAPTURE_DRV_NAME, - (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, - (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, - (VPFE_CAPTURE_VERSION_CODE) & 0xff); - - ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); - if (ret) { - v4l2_err(pdev->dev.driver, - "Unable to register v4l2 device.\n"); - goto probe_out_release_irq; - } - v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); - spin_lock_init(&vpfe_dev->irqlock); - spin_lock_init(&vpfe_dev->dma_queue_lock); - mutex_init(&vpfe_dev->lock); - - /* Initialize field of the device objects */ - vpfe_dev->numbuffers = config_params.numbuffers; - - /* register video device */ - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "trying to register vpfe device.\n"); - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "video_dev=%p\n", &vpfe_dev->video_dev); - vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ret = video_register_device(&vpfe_dev->video_dev, - VFL_TYPE_VIDEO, -1); - - if (ret) { - v4l2_err(pdev->dev.driver, - "Unable to register video device.\n"); - goto probe_out_v4l2_unregister; - } - - v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); - /* set the driver data in platform device */ - platform_set_drvdata(pdev, vpfe_dev); - /* set driver private data */ - video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev); - i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); - num_subdevs = vpfe_cfg->num_subdevs; - vpfe_dev->sd = kmalloc_array(num_subdevs, - sizeof(*vpfe_dev->sd), - GFP_KERNEL); - if (!vpfe_dev->sd) { - ret = -ENOMEM; - goto probe_out_video_unregister; - } - - for (i = 0; i < num_subdevs; i++) { - struct v4l2_input *inps; - - sdinfo = &vpfe_cfg->sub_devs[i]; - - /* Load up the subdevice */ - vpfe_dev->sd[i] = - v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, - i2c_adap, - &sdinfo->board_info, - NULL); - if (vpfe_dev->sd[i]) { - v4l2_info(&vpfe_dev->v4l2_dev, - "v4l2 sub device %s registered\n", - sdinfo->name); - vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; - /* update tvnorms from the sub devices */ - for (j = 0; j < sdinfo->num_inputs; j++) { - inps = &sdinfo->inputs[j]; - vfd->tvnorms |= inps->std; - } - } else { - v4l2_info(&vpfe_dev->v4l2_dev, - "v4l2 sub device %s register fails\n", - sdinfo->name); - ret = -ENXIO; - goto probe_sd_out; - } - } - - /* set first sub device as current one */ - vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; - vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler; - - /* We have at least one sub device to work with */ - mutex_unlock(&ccdc_lock); - return 0; - -probe_sd_out: - kfree(vpfe_dev->sd); -probe_out_video_unregister: - video_unregister_device(&vpfe_dev->video_dev); -probe_out_v4l2_unregister: - v4l2_device_unregister(&vpfe_dev->v4l2_dev); -probe_out_release_irq: - free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); -probe_free_ccdc_cfg_mem: - kfree(ccdc_cfg); - mutex_unlock(&ccdc_lock); -probe_free_dev_mem: - kfree(vpfe_dev); - return ret; -} - -/* - * vpfe_remove : It un-register device from V4L2 driver - */ -static int vpfe_remove(struct platform_device *pdev) -{ - struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); - - v4l2_info(pdev->dev.driver, "vpfe_remove\n"); - - free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); - kfree(vpfe_dev->sd); - v4l2_device_unregister(&vpfe_dev->v4l2_dev); - video_unregister_device(&vpfe_dev->video_dev); - kfree(vpfe_dev); - kfree(ccdc_cfg); - return 0; -} - -static int vpfe_suspend(struct device *dev) -{ - return 0; -} - -static int vpfe_resume(struct device *dev) -{ - return 0; -} - -static const struct dev_pm_ops vpfe_dev_pm_ops = { - .suspend = vpfe_suspend, - .resume = vpfe_resume, -}; - -static struct platform_driver vpfe_driver = { - .driver = { - .name = CAPTURE_DRV_NAME, - .pm = &vpfe_dev_pm_ops, - }, - .probe = vpfe_probe, - .remove = vpfe_remove, -}; - -module_platform_driver(vpfe_driver); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 15e92b3c6342..e520241e7723 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -60,6 +60,7 @@ source "drivers/staging/media/deprecated/fsl-viu/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" source "drivers/staging/media/deprecated/tm6000/Kconfig" +source "drivers/staging/media/deprecated/vpfe_capture/Kconfig" source "drivers/staging/media/deprecated/zr364xx/Kconfig" endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 10f7844b6681..ad2893d34cb8 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ obj-$(CONFIG_DVB_AV7110) += av7110/ +obj-y += deprecated/vpfe_capture/ diff --git a/drivers/staging/media/deprecated/vpfe_capture/Kconfig b/drivers/staging/media/deprecated/vpfe_capture/Kconfig new file mode 100644 index 000000000000..10250e7e566b --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/Kconfig @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_DM6446_CCDC + tristate "TI DM6446 CCDC video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and dm644x_ccdc.ko + +config VIDEO_DM355_CCDC + tristate "TI DM355 CCDC video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and dm355_ccdc.ko + +config VIDEO_DM365_ISIF + tristate "TI DM365 ISIF video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables ISIF hw module. This is the hardware module for + configuring ISIF in VPFE to capture Raw Bayer RGB data from + a image sensor or YUV data from a YUV source. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and isif.ko diff --git a/drivers/staging/media/deprecated/vpfe_capture/Makefile b/drivers/staging/media/deprecated/vpfe_capture/Makefile new file mode 100644 index 000000000000..609e8dc09ce7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o dm355_ccdc.o +obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o isif.o diff --git a/drivers/staging/media/deprecated/vpfe_capture/TODO b/drivers/staging/media/deprecated/vpfe_capture/TODO new file mode 100644 index 000000000000..ce654d7337af --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/TODO @@ -0,0 +1,7 @@ +These are one of the few drivers still not using the vb2 +framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h b/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h new file mode 100644 index 000000000000..a545052a95a9 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include +#include +#include +#include + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev); + +#endif +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c new file mode 100644 index 000000000000..da8db53e9498 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include + +#include "dm355_ccdc.h" +#include + +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gamma_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, + }, + /* YCbCr configuration */ + .ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED + }, +}; + + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_cfg.base_addr + offset); +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); + regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); + regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); + regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1 = 0, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int val; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + u32 pixfmt; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; + else + ccdc_cfg.ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int len; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_cfg.bayer.win.width; + else + len = ccdc_cfg.bayer.win.width * 2; + } else + len = ccdc_cfg.ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static const struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_probe(struct platform_device *pdev) +{ + void (*setup_pinmux)(void); + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + /* Platform data holds setup_pinmux function ptr */ + if (NULL == pdev->dev.platform_data) { + status = -ENODEV; + goto fail_nomap; + } + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); + return 0; +fail_nomap: + iounmap(ccdc_cfg.base_addr); +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; +} + +static int dm355_ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static struct platform_driver dm355_ccdc_driver = { + .driver = { + .name = "dm355_ccdc", + }, + .remove = dm355_ccdc_remove, + .probe = dm355_ccdc_probe, +}; + +module_platform_driver(dm355_ccdc_driver); diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h new file mode 100644 index 000000000000..1f3d00aa46d1 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + */ +#ifndef _DM355_CCDC_H +#define _DM355_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping */ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gamma width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +enum ccdc_datasft { + CCDC_DATA_NO_SHIFT, + CCDC_DATA_SHIFT_1BIT, + CCDC_DATA_SHIFT_2BIT, + CCDC_DATA_SHIFT_3BIT, + CCDC_DATA_SHIFT_4BIT, + CCDC_DATA_SHIFT_5BIT, + CCDC_DATA_SHIFT_6BIT +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; +enum ccdc_mfilt1 { + CCDC_NO_MEDIAN_FILTER1, + CCDC_AVERAGE_FILTER1, + CCDC_MEDIAN_FILTER1 +}; + +enum ccdc_mfilt2 { + CCDC_NO_MEDIAN_FILTER2, + CCDC_AVERAGE_FILTER2, + CCDC_MEDIAN_FILTER2 +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + /* only if bClampEnable is TRUE */ + unsigned char b_clamp_enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is FALSE */ + unsigned short sgain; + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + unsigned char r; + /* Constant value to subtract from Gr component */ + unsigned char gr; + /* Constant value to subtract from Blue component */ + unsigned char b; + /* Constant value to subtract from Gb component */ + unsigned char gb; +}; + +struct ccdc_float { + int integer; + unsigned int decimal; +}; + +#define CCDC_CSC_COEFF_TABLE_SIZE 16 +/* structure for color space converter */ +struct ccdc_csc { + unsigned char enable; + /* + * S8Q5. Use 2 decimal precision, user values range from -3.00 to 3.99. + * example - to use 1.03, set integer part as 1, and decimal part as 3 + * to use -1.03, set integer part as -1 and decimal part as 3 + */ + struct ccdc_float coeff[CCDC_CSC_COEFF_TABLE_SIZE]; +}; + +/* Structures for Vertical Defect Correction*/ +enum ccdc_vdf_csl { + CCDC_VDF_NORMAL, + CCDC_VDF_HORZ_INTERPOL_SAT, + CCDC_VDF_HORZ_INTERPOL +}; + +enum ccdc_vdf_cuda { + CCDC_VDF_WHOLE_LINE_CORRECT, + CCDC_VDF_UPPER_DISABLE +}; + +enum ccdc_dfc_mwr { + CCDC_DFC_MWR_WRITE_COMPLETE, + CCDC_DFC_WRITE_REG +}; + +enum ccdc_dfc_mrd { + CCDC_DFC_READ_COMPLETE, + CCDC_DFC_READ_REG +}; + +enum ccdc_dfc_ma_rst { + CCDC_DFC_INCR_ADDR, + CCDC_DFC_CLR_ADDR +}; + +enum ccdc_dfc_mclr { + CCDC_DFC_CLEAR_COMPLETE, + CCDC_DFC_CLEAR +}; + +struct ccdc_dft_corr_ctl { + enum ccdc_vdf_csl vdfcsl; + enum ccdc_vdf_cuda vdfcuda; + unsigned int vdflsft; +}; + +struct ccdc_dft_corr_mem_ctl { + enum ccdc_dfc_mwr dfcmwr; + enum ccdc_dfc_mrd dfcmrd; + enum ccdc_dfc_ma_rst dfcmarst; + enum ccdc_dfc_mclr dfcmclr; +}; + +#define CCDC_DFT_TABLE_SIZE 16 +/* + * Main Structure for vertical defect correction. Vertical defect + * correction can correct up to 16 defects if defects less than 16 + * then pad the rest with 0 + */ +struct ccdc_vertical_dft { + unsigned char ver_dft_en; + unsigned char gen_dft_en; + unsigned int saturation_ctl; + struct ccdc_dft_corr_ctl dft_corr_ctl; + struct ccdc_dft_corr_mem_ctl dft_corr_mem_ctl; + int table_size; + unsigned int dft_corr_horz[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_vert[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub1[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub2[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub3[CCDC_DFT_TABLE_SIZE]; +}; + +struct ccdc_data_offset { + unsigned char horz_offset; + unsigned char vert_offset; +}; + +/* + * Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data shift to be applied before storing */ + enum ccdc_datasft datasft; + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* median filter for sdram */ + enum ccdc_mfilt1 mfilt1; + enum ccdc_mfilt2 mfilt2; + /* low pass filter enable/disable */ + unsigned char lpf_enable; + /* Threshold of median filter */ + int med_filt_thres; + /* + * horz and vertical data offset. Applicable for defect correction + * and lsc + */ + struct ccdc_data_offset data_offset; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* structure for vertical Defect Correction Module Configuration */ + struct ccdc_vertical_dft vertical_dft; + /* structure for color space converter Module Configuration */ + struct ccdc_csc csc; + /* color patters for bayer capture */ + struct ccdc_col_pat col_pat_field0; + struct ccdc_col_pat col_pat_field1; +}; + +#ifdef __KERNEL__ +#include + +#define CCDC_WIN_PAL {0, 0, 720, 576} +#define CCDC_WIN_VGA {0, 0, 640, 480} + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* Gain applied to Raw Bayer data */ +struct ccdc_gain { + unsigned short r_ye; + unsigned short gr_cy; + unsigned short gb_g; + unsigned short b_mg; +}; + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* offset */ + unsigned int ccdc_offset; + /* horizontal flip enable */ + unsigned char horz_flip_enable; + /* + * enable to store the image in inverse order in memory + * (bottom to top) + */ + unsigned char image_invert_enable; + /* Configurable part of raw data */ + struct ccdc_config_params_raw config_params; +}; + +#endif +#endif /* DM355_CCDC_H */ diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h new file mode 100644 index 000000000000..eb381f075245 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c new file mode 100644 index 000000000000..4a93e5ad6415 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. + * This file is named DM644x so that other variants such DM6443 + * may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include +#include +#include +#include +#include +#include + +#include "dm644x_ccdc.h" +#include + +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, + }, + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED + }, +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* CCDC Save/Restore context */ +static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_cfg.base_addr + offset); +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int ccdc_close(struct device *dev) +{ + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + ccdc_restore_defaults(); + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; + u32 syn_mode; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= CCDC_SYN_MODE_10BITS; + else + syn_mode |= CCDC_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, + CCDC_CCDCFG); + else + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte boundary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +static void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_cfg.bayer.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + u32 pixfmt; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; + else + ccdc_cfg.ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int len; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_cfg.bayer.win.width; + else + len = ccdc_cfg.bayer.win.width * 2; + } else + len = ccdc_cfg.ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static void ccdc_save_context(void) +{ + ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); + ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); + ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); + ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); + ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); + ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); + ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); + ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); + ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); + ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); + ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); + ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); + ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); + ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); + ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); + ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); + ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); + ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); + ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); + ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); + ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); + ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); + ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); + ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); + ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); + ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); + ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); + ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); + ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); + ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); + ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); + ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); + ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); + ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); + ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); + ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); + ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); +} + +static void ccdc_restore_context(void) +{ + regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); + regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); + regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); + regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); + regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); + regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); + regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); + regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); + regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); + regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); + regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); + regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); + regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); + regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); + regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); + regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); + regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); + regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); + regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); + regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); + regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); + regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); + regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); + regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); + regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); + regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); + regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); + regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); + regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); + regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); + regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); + regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); + regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); + regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); + regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); + regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); + regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); +} +static const struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm644x_ccdc_probe(struct platform_device *pdev) +{ + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); + return 0; +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; +} + +static int dm644x_ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static int dm644x_ccdc_suspend(struct device *dev) +{ + /* Save CCDC context */ + ccdc_save_context(); + /* Disable CCDC */ + ccdc_enable(0); + + return 0; +} + +static int dm644x_ccdc_resume(struct device *dev) +{ + /* Restore CCDC context */ + ccdc_restore_context(); + + return 0; +} + +static const struct dev_pm_ops dm644x_ccdc_pm_ops = { + .suspend = dm644x_ccdc_suspend, + .resume = dm644x_ccdc_resume, +}; + +static struct platform_driver dm644x_ccdc_driver = { + .driver = { + .name = "dm644x_ccdc", + .pm = &dm644x_ccdc_pm_ops, + }, + .remove = dm644x_ccdc_remove, + .probe = dm644x_ccdc_probe, +}; + +module_platform_driver(dm644x_ccdc_driver); diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h new file mode 100644 index 000000000000..c20dba3d76d6 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + */ +#ifndef _DM644X_CCDC_H +#define _DM644X_CCDC_H +#include +#include + +/* enum for No of pixel per line to be avg. in Black Clamping*/ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gamma width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_15_6, /* use bits 15-6 for gamma */ + CCDC_GAMMA_BITS_14_5, + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 /* use bits 9-0 for gamma */ +}; + +/* returns the highest bit used for the gamma */ +static inline u8 ccdc_gamma_width_max_bit(enum ccdc_gamma_width width) +{ + return 15 - width; +} + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; + +/* returns the highest bit used for this data size */ +static inline u8 ccdc_data_size_max_bit(enum ccdc_data_size sz) +{ + return sz == CCDC_DATA_8BITS ? 7 : 15 - sz; +} + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + unsigned char enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is TRUE */ + unsigned short sgain; + /* only if bClampEnable is FALSE */ + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + char r; + /* Constant value to subtract from Gr component */ + char gr; + /* Constant value to subtract from Blue component */ + char b; + /* Constant value to subtract from Gb component */ + char gb; +}; + +/* Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; +}; + + +#ifdef __KERNEL__ +#include +/* Define to enable/disable video port */ +#define FP_NUM_BYTES 4 +/* Define for extra pixel/line and extra lines/frame */ +#define NUM_EXTRAPIXELS 8 +#define NUM_EXTRALINES 8 + +/* settings for commonly used video formats */ +#define CCDC_WIN_PAL {0, 0, 720, 576} +/* ntsc square pixel */ +#define CCDC_WIN_VGA {0, 0, (640 + NUM_EXTRAPIXELS), (480 + NUM_EXTRALINES)} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable parameters */ + struct ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; +#endif +#endif /* _DM644X_CCDC_H */ diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h new file mode 100644 index 000000000000..c4894f6a254e --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 +#define CCDC_REG_END 0x98 + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE BIT(17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE BIT(16) +#define CCDC_LPF_ENABLE BIT(14) +#define CCDC_ALAW_ENABLE BIT(3) +#define CCDC_ALAW_GAMMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE BIT(31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE BIT(15) +#define CCDC_FPC_ENABLE BIT(15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE BIT(11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_MODE_10BITS (6 << 8) +#define CCDC_SYN_MODE_11BITS (5 << 8) +#define CCDC_SYN_MODE_12BITS (4 << 8) +#define CCDC_SYN_MODE_13BITS (3 << 8) +#define CCDC_SYN_MODE_14BITS (2 << 8) +#define CCDC_SYN_MODE_15BITS (1 << 8) +#define CCDC_SYN_MODE_16BITS (0 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE BIT(2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_CCDCFG_BW656_10BIT BIT(5) +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif.c b/drivers/staging/media/deprecated/vpfe_capture/isif.c new file mode 100644 index 000000000000..4059891c2824 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif.c @@ -0,0 +1,1127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Image Sensor Interface (ISIF) driver + * + * This driver is for configuring the ISIF IP available on DM365 or any other + * TI SoCs. This is used for capturing yuv or bayer video or image data + * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 + * and DM6446, but with enhanced or additional ip blocks. The driver + * configures the ISIF upon commands from the vpfe bridge driver through + * ccdc_hw_device interface. + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Add support for control ioctl + */ +#include +#include +#include +#include +#include +#include +#include + +#include "isif.h" +#include + +#include "isif_regs.h" +#include "ccdc_hw_device.h" + +/* Defaults for module configuration parameters */ +static const struct isif_config_params_raw isif_config_defaults = { + .linearize = { + .en = 0, + .corr_shft = ISIF_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .df_csc = { + .df_or_csc = 0, + .csc = { + .en = 0, + }, + }, + .dfc = { + .en = 0, + }, + .bclamp = { + .en = 0, + }, + .gain_offset = { + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + }, + .culling = { + .hcpat_odd = 0xff, + .hcpat_even = 0xff, + .vcpat = 0xff, + }, + .compress = { + .alg = ISIF_ALAW, + }, +}; + +/* ISIF operation configuration */ +static struct isif_oper_config { + struct device *dev; + enum vpfe_hw_if_type if_type; + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + /* ISIF base address */ + void __iomem *base_addr; + /* ISIF Linear Table 0 */ + void __iomem *linear_tbl0_addr; + /* ISIF Linear Table 1 */ + void __iomem *linear_tbl1_addr; +} isif_cfg = { + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = ISIF_WIN_NTSC, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, + }, + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = ISIF_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + .cfa_pat = ISIF_CFA_PAT_MOSAIC, + .data_msb = ISIF_BIT_MSB_11, + .config_params = { + .data_shift = ISIF_NO_SHIFT, + .col_pat_field0 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .col_pat_field1 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .test_pat_gen = 0, + }, + }, + .data_pack = ISIF_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static const u32 isif_raw_bayer_pix_formats[] = { + V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static const u32 isif_raw_yuv_pix_formats[] = { + V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(isif_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, isif_cfg.base_addr + offset); +} + +/* reg_modify() - read, modify and write register */ +static inline u32 reg_modify(u32 mask, u32 val, u32 offset) +{ + u32 new_val = (regr(offset) & ~mask) | (val & mask); + + regw(new_val, offset); + return new_val; +} + +static inline void regw_lin_tbl(u32 val, u32 offset, int i) +{ + if (!i) + __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); + else + __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(void) +{ + /* disable BC */ + regw(0, CLAMPCFG); + /* disable vdfc */ + regw(0, DFCCTL); + /* disable CSC */ + regw(0, CSCCTL); + /* disable linearization */ + regw(0, LINCFG0); + /* disable other modules here as they are supported */ +} + +static void isif_enable(int en) +{ + if (!en) { + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(); + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + } + reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); +} + +static void isif_enable_output_to_sdram(int en) +{ + reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static void isif_config_culling(struct isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; + regw(val, CULH); + + /* vertical pattern */ + regw(cul->vcpat, CULV); + + /* LPF */ + reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static void isif_config_gain_offset(void) +{ + struct isif_gain_offsets_adj *gain_off_p = + &isif_cfg.bayer.config_params.gain_offset; + u32 val; + + val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | + (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | + (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | + (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | + (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | + (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); + + reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.r_ye.decimal; + regw(val, CRGAIN); + + val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gr_cy.decimal; + regw(val, CGRGAIN); + + val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gb_g.decimal; + regw(val, CGBGAIN); + + val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.b_mg.decimal; + regw(val, CBGAIN); + + regw(gain_off_p->offset, COFSTA); +} + +static void isif_restore_defaults(void) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + + dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); + isif_cfg.bayer.config_params = isif_config_defaults; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + /* Set default offset and gain */ + isif_config_gain_offset(); + vpss_select_ccdc_source(source); + dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); +} + +static int isif_open(struct device *device) +{ + isif_restore_defaults(); + return 0; +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void isif_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start & START_PX_HOR_MASK, SPH); + regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(mid_img, VDINT1); + } + + regw(0, VDINT0); + regw(vert_start & START_VER_ONE_MASK, SLV0); + regw(vert_start & START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & NUM_LINES_VER, LNV); +} + +static void isif_config_bclamp(struct isif_black_clamp *bc) +{ + u32 val; + + /* + * DC Offset is always added to image data irrespective of bc enable + * status + */ + regw(bc->dc_offset, CLDCOFST); + + if (bc->en) { + val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp calculation parameters */ + val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); + + regw(val, CLAMPCFG); + + if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = bc->horz.win_count_calc | + ((!!bc->horz.base_win_sel_calc) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((!!bc->horz.clamp_pix_limit) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + (bc->horz.win_h_sz_calc << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + (bc->horz.win_v_sz_calc << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + regw(val, CLHWIN0); + + regw(bc->horz.win_start_h_calc, CLHWIN1); + regw(bc->horz.win_start_v_calc, CLHWIN2); + } + + /* vertical clamp calculation parameters */ + + /* Reset clamp value sel for previous line */ + val |= + (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | + (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); + regw(val, CLVWIN0); + + /* Optical Black horizontal start position */ + regw(bc->vert.ob_start_h, CLVWIN1); + /* Optical Black vertical start position */ + regw(bc->vert.ob_start_v, CLVWIN2); + /* Optical Black vertical size for calculation */ + regw(bc->vert.ob_v_sz_calc, CLVWIN3); + /* Vertical start position for BC subtraction */ + regw(bc->vert_start_sub, CLSV); + } +} + +static void isif_config_linearization(struct isif_linearize *linearize) +{ + u32 val, i; + + if (!linearize->en) { + regw(0, LINCFG0); + return; + } + + /* shift value for correction & enable linearization (set lsb) */ + val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; + regw(val, LINCFG0); + + /* Scale factor */ + val = ((!!linearize->scale_fact.integer) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | + linearize->scale_fact.decimal; + regw(val, LINCFG1); + + for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { + if (i % 2) + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); + else + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); + } +} + +static int isif_config_dfc(struct isif_dfc *vdfc) +{ + /* initialize retries to loop for max ~ 250 usec */ + u32 val, count, retries = loops_per_jiffy / (4000/HZ); + int i; + + if (!vdfc->en) + return 0; + + /* Correction mode */ + val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; + + regw(val, DFCCTL); + + /* Defect saturation level */ + regw(vdfc->def_sat_level, VDFSATLV); + + regw(vdfc->table[0].pos_vert, DFCMEM0); + regw(vdfc->table[0].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[0].level_at_pos, DFCMEM2); + regw(vdfc->table[0].level_up_pixels, DFCMEM3); + regw(vdfc->table[0].level_low_pixels, DFCMEM4); + } + + /* set DFCMARST and set DFCMWR */ + val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); + return -1; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + regw(vdfc->table[i].pos_vert, DFCMEM0); + regw(vdfc->table[i].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[i].level_at_pos, DFCMEM2); + regw(vdfc->table[i].level_up_pixels, DFCMEM3); + regw(vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = regr(DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_err(isif_cfg.dev, + "defect table write timeout !!!\n"); + return -1; + } + } + if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + regw(0, DFCMEM0); + regw(0x1FFF, DFCMEM1); + regw(1, DFCMEMCTL); + } + + /* enable VDFC */ + reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), + DFCCTL); + return 0; +} + +static void isif_config_csc(struct isif_df_csc *df_csc) +{ + u32 val1 = 0, val2 = 0, i; + + if (!df_csc->csc.en) { + regw(0, CSCCTL); + return; + } + for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + } else { + + /* CSCM - MSB */ + val2 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } + + /* program the active area */ + regw(df_csc->start_pix, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + regw(df_csc->num_pixels, FMTLNH); + regw(df_csc->start_line, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + regw(df_csc->num_lines, FMTLNV); + + /* Enable CSC */ + regw(1, CSCCTL); +} + +static int isif_config_raw(void) +{ + struct isif_params_raw *params = &isif_cfg.bayer; + struct isif_config_params_raw *module_params = + &isif_cfg.bayer.config_params; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 val; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); + + /* + * Configure CCDCFG register:- + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; + + dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); + regw(val, CCDCFG); + + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + + val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | + (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->config_params.data_shift << ISIF_DATASFT_SHIFT); + + regw(val, MODESET); + dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); + + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; + + /* Gamma msb */ + if (module_params->compress.alg == ISIF_ALAW) + val |= ISIF_ALAW_ENABLE; + + val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); + regw(val, CGAMMAWD); + + /* Configure DPCM compression settings */ + if (module_params->compress.alg == ISIF_DPCM) { + val = BIT(ISIF_DPCM_EN_SHIFT) | + (module_params->compress.pred << + ISIF_DPCM_PREDICTOR_SHIFT); + } + + regw(val, MISC); + + /* Configure Gain & Offset */ + isif_config_gain_offset(); + + /* Configure Color pattern */ + val = (params->config_params.col_pat_field0.olop) | + (params->config_params.col_pat_field0.olep << 2) | + (params->config_params.col_pat_field0.elop << 4) | + (params->config_params.col_pat_field0.elep << 6) | + (params->config_params.col_pat_field1.olop << 8) | + (params->config_params.col_pat_field1.olep << 10) | + (params->config_params.col_pat_field1.elop << 12) | + (params->config_params.col_pat_field1.elep << 14); + regw(val, CCOLP); + dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); + + /* Configure HSIZE register */ + val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= (((params->win.width + + (params->win.width >> 2)) + 31) >> 5); + else + val |= (((params->win.width * 2) + 31) >> 5); + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_en) { + /* For interlace inverse mode */ + regw(0x4B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); + } else { + /* For interlace non inverse mode */ + regw(0x0B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) { + /* For progressive inverse mode */ + regw(0x4000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); + } else { + /* For progressive non inverse mode */ + regw(0x0000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); + } + } + + /* Configure video window */ + isif_setwin(¶ms->win, params->frm_fmt, 1); + + /* Configure Black Clamp */ + isif_config_bclamp(&module_params->bclamp); + + /* Configure Vertical Defection Pixel Correction */ + if (isif_config_dfc(&module_params->dfc) < 0) + return -EFAULT; + + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(&module_params->df_csc); + + isif_config_linearization(&module_params->linearize); + + /* Configure Culling */ + isif_config_culling(&module_params->culling); + + /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ + regw(module_params->horz_offset, DATAHOFST); + regw(module_params->vert_offset, DATAVOFST); + + /* Setup test pattern if enabled */ + if (params->config_params.test_pat_gen) { + /* Use the HD/VD pol settings from user */ + sync.ccdpg_hdpol = params->hd_pol; + sync.ccdpg_vdpol = params->vd_pol; + dm365_vpss_set_sync_pol(sync); + frame_size.hlpfr = isif_cfg.bayer.win.width; + frame_size.pplen = isif_cfg.bayer.win.height; + dm365_vpss_set_pg_frame_size(frame_size); + vpss_select_ccdc_source(VPSS_PGLPBK); + } + + dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); + return 0; +} + +static int isif_set_buftype(enum ccdc_buftype buf_type) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.buf_type = buf_type; + else + isif_cfg.ycbcr.buf_type = buf_type; + + return 0; + +} +static enum ccdc_buftype isif_get_buftype(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.buf_type; + + return isif_cfg.ycbcr.buf_type; +} + +static int isif_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { + *pix = isif_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { + *pix = isif_raw_yuv_pix_formats[i]; + ret = 0; + } + } + + return ret; +} + +static int isif_set_pixel_format(unsigned int pixfmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (pixfmt == V4L2_PIX_FMT_SBGGR8) { + if ((isif_cfg.bayer.config_params.compress.alg != + ISIF_ALAW) && + (isif_cfg.bayer.config_params.compress.alg != + ISIF_DPCM)) { + dev_dbg(isif_cfg.dev, + "Either configure A-Law or DPCM\n"); + return -EINVAL; + } + isif_cfg.data_pack = ISIF_PACK_8BIT; + } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { + isif_cfg.bayer.config_params.compress.alg = + ISIF_NO_COMPRESSION; + isif_cfg.data_pack = ISIF_PACK_16BIT; + } else + return -EINVAL; + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + isif_cfg.data_pack = ISIF_PACK_8BIT; + } + return 0; +} + +static u32 isif_get_pixel_format(void) +{ + u32 pixfmt; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) + if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || + isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int isif_set_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + isif_cfg.bayer.win.top = win->top; + isif_cfg.bayer.win.left = win->left; + isif_cfg.bayer.win.width = win->width; + isif_cfg.bayer.win.height = win->height; + } else { + isif_cfg.ycbcr.win.top = win->top; + isif_cfg.ycbcr.win.left = win->left; + isif_cfg.ycbcr.win.width = win->width; + isif_cfg.ycbcr.win.height = win->height; + } + return 0; +} + +static void isif_get_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + *win = isif_cfg.bayer.win; + else + *win = isif_cfg.ycbcr.win; +} + +static unsigned int isif_get_line_length(void) +{ + unsigned int len; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + len = ((isif_cfg.bayer.win.width)); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + len = (((isif_cfg.bayer.win.width * 2) + + (isif_cfg.bayer.win.width >> 2))); + else + len = (((isif_cfg.bayer.win.width * 2))); + } else + len = (((isif_cfg.ycbcr.win.width * 2))); + return ALIGN(len, 32); +} + +static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} +static enum ccdc_frmfmt isif_get_frame_format(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.frm_fmt; + return isif_cfg.ycbcr.frm_fmt; +} + +static int isif_getfid(void) +{ + return (regr(MODESET) >> 15) & 0x1; +} + +/* misc operations */ +static void isif_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x07ff, CADU); + regw((addr >> 5) & 0x0ffff, CADL); +} + +static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + isif_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_BT656_10BIT: + case VPFE_YCBCR_SYNC_8: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_BT1120: + case VPFE_YCBCR_SYNC_16: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_RAW_BAYER: + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + break; + default: + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + return 0; +} + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(void) +{ + struct isif_ycbcr_config *params = &isif_cfg.ycbcr; + u32 modeset = 0, ccdcfg = 0; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); + + /* configure pixel format or input mode */ + modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->vd_pol << ISIF_VD_POL_SHIFT); + + /* pack the data to 8-bit ISIFCFG */ + switch (isif_cfg.if_type) { + case VPFE_BT656: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); + regw(3, REC656IF); + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; + break; + case VPFE_BT656_10BIT: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + regw(3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + case VPFE_BT1120: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + regw(3, REC656IF); + break; + + case VPFE_YCBCR_SYNC_8: + ccdcfg |= ISIF_DATA_PACK8; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + case VPFE_YCBCR_SYNC_16: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + default: + /* should never come here */ + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + regw(modeset, MODESET); + + /* Set up pix order */ + ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; + + regw(ccdcfg, CCDCFG); + + /* configure video window */ + if ((isif_cfg.if_type == VPFE_BT1120) || + (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) + isif_setwin(¶ms->win, params->frm_fmt, 1); + else + isif_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); + + /* configure the memory line offset */ + if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && + (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) + /* two fields are interleaved in memory */ + regw(0x00000249, SDOFST); + + return 0; +} + +static int isif_configure(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_config_raw(); + return isif_config_ycbcr(); +} + +static int isif_close(struct device *device) +{ + /* copy defaults to module params */ + isif_cfg.bayer.config_params = isif_config_defaults; + return 0; +} + +static const struct ccdc_hw_device isif_hw_dev = { + .name = "ISIF", + .owner = THIS_MODULE, + .hw_ops = { + .open = isif_open, + .close = isif_close, + .enable = isif_enable, + .enable_out_to_sdram = isif_enable_output_to_sdram, + .set_hw_if_params = isif_set_hw_if_params, + .configure = isif_configure, + .set_buftype = isif_set_buftype, + .get_buftype = isif_get_buftype, + .enum_pix = isif_enum_pix, + .set_pixel_format = isif_set_pixel_format, + .get_pixel_format = isif_get_pixel_format, + .set_frame_format = isif_set_frame_format, + .get_frame_format = isif_get_frame_format, + .set_image_window = isif_set_image_window, + .get_image_window = isif_get_image_window, + .get_line_length = isif_get_line_length, + .setfbaddr = isif_setfbaddr, + .getfid = isif_getfid, + }, +}; + +static int isif_probe(struct platform_device *pdev) +{ + void (*setup_pinmux)(void); + struct resource *res; + void __iomem *addr; + int status = 0, i; + + /* Platform data holds setup_pinmux function ptr */ + if (!pdev->dev.platform_data) + return -ENODEV; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&isif_hw_dev); + if (status < 0) + return status; + + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + + i = 0; + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENODEV; + goto fail_nobase_res; + } + res = request_mem_region(res->start, resource_size(res), + res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap(res->start, resource_size(res)); + if (!addr) { + status = -ENOMEM; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + isif_cfg.dev = &pdev->dev; + + printk(KERN_NOTICE "%s is registered with vpfe.\n", + isif_hw_dev.name); + return 0; +fail_base_iomap: + release_mem_region(res->start, resource_size(res)); + i--; +fail_nobase_res: + if (isif_cfg.base_addr) { + iounmap(isif_cfg.base_addr); + isif_cfg.base_addr = NULL; + } + if (isif_cfg.linear_tbl0_addr) { + iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; + } + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, resource_size(res)); + i--; + } + vpfe_unregister_ccdc_device(&isif_hw_dev); + return status; +} + +static int isif_remove(struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif_cfg.base_addr); + isif_cfg.base_addr = NULL; + iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; + iounmap(isif_cfg.linear_tbl1_addr); + isif_cfg.linear_tbl1_addr = NULL; + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, resource_size(res)); + i++; + } + vpfe_unregister_ccdc_device(&isif_hw_dev); + return 0; +} + +static struct platform_driver isif_driver = { + .driver = { + .name = "isif", + }, + .remove = isif_remove, + .probe = isif_probe, +}; + +module_platform_driver(isif_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif.h b/drivers/staging/media/deprecated/vpfe_capture/isif.h new file mode 100644 index 000000000000..8369acd26e7e --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif.h @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * isif header file + */ +#ifndef _ISIF_H +#define _ISIF_H + +#include +#include + +/* isif float type S8Q8/U8Q8 */ +struct isif_float_8 { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +/* isif float type U16Q16/S16Q16 */ +struct isif_float_16 { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ +/* Defect Correction (DFC) table entry */ +struct isif_vdfc_entry { + /* vertical position of defect */ + __u16 pos_vert; + /* horizontal position of defect */ + __u16 pos_horz; + /* + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + __u8 level_at_pos; + /* + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_up_pixels; + /* + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_low_pixels; +}; + +#define ISIF_VDFC_TABLE_SIZE 8 +struct isif_dfc { + /* enable vertical defect correction */ + __u8 en; + /* Defect level subtraction. Just fed through if saturating */ +#define ISIF_VDFC_NORMAL 0 + /* + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ +#define ISIF_VDFC_HORZ_INTERPOL_IF_SAT 1 + /* Horizontal interpolation (((i-2)+(i+2))/2) */ +#define ISIF_VDFC_HORZ_INTERPOL 2 + /* one of the vertical defect correction modes above */ + __u8 corr_mode; + /* 0 - whole line corrected, 1 - not pixels upper than the defect */ + __u8 corr_whole_line; +#define ISIF_VDFC_NO_SHIFT 0 +#define ISIF_VDFC_SHIFT_1 1 +#define ISIF_VDFC_SHIFT_2 2 +#define ISIF_VDFC_SHIFT_3 3 +#define ISIF_VDFC_SHIFT_4 4 + /* + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value. Choose + * one of the values above + */ + __u8 def_level_shift; + /* defect saturation level */ + __u16 def_sat_level; + /* number of vertical defects. Max is ISIF_VDFC_TABLE_SIZE */ + __u16 num_vdefects; + /* VDFC table ptr */ + struct isif_vdfc_entry table[ISIF_VDFC_TABLE_SIZE]; +}; + +struct isif_horz_bclamp { + + /* Horizontal clamp disabled. Only vertical clamp value is subtracted */ +#define ISIF_HORZ_BC_DISABLE 0 + /* + * Horizontal clamp value is calculated and subtracted from image data + * along with vertical clamp value + */ +#define ISIF_HORZ_BC_CLAMP_CALC_ENABLED 1 + /* + * Horizontal clamp value calculated from previous image is subtracted + * from image data along with vertical clamp value. + */ +#define ISIF_HORZ_BC_CLAMP_NOT_UPDATED 2 + /* horizontal clamp mode. One of the values above */ + __u8 mode; + /* + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + __u8 clamp_pix_limit; + /* Select Most left window for bc calculation */ +#define ISIF_SEL_MOST_LEFT_WIN 0 + /* Select Most right window for bc calculation */ +#define ISIF_SEL_MOST_RIGHT_WIN 1 + /* Select most left or right window for clamp val calculation */ + __u8 base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + __u8 win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + __u16 win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + __u16 win_start_v_calc; +#define ISIF_HORZ_BC_SZ_H_2PIXELS 0 +#define ISIF_HORZ_BC_SZ_H_4PIXELS 1 +#define ISIF_HORZ_BC_SZ_H_8PIXELS 2 +#define ISIF_HORZ_BC_SZ_H_16PIXELS 3 + /* Width of the sample window in pixels for calculation */ + __u8 win_h_sz_calc; +#define ISIF_HORZ_BC_SZ_V_32PIXELS 0 +#define ISIF_HORZ_BC_SZ_V_64PIXELS 1 +#define ISIF_HORZ_BC_SZ_V_128PIXELS 2 +#define ISIF_HORZ_BC_SZ_V_256PIXELS 3 + /* Height of the sample window in pixels for calculation */ + __u8 win_v_sz_calc; +}; + +/************************************************************************ + * Black Clamp parameters + ***********************************************************************/ +struct isif_vert_bclamp { + /* Reset value used is the clamp value calculated */ +#define ISIF_VERT_BC_USE_HORZ_CLAMP_VAL 0 + /* Reset value used is reset_clamp_val configured */ +#define ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL 1 + /* No update, previous image value is used */ +#define ISIF_VERT_BC_NO_UPDATE 2 + /* + * Reset value selector for vertical clamp calculation. Use one of + * the above values + */ + __u8 reset_val_sel; + /* U8Q8. Line average coefficient used in vertical clamp calculation */ + __u8 line_ave_coef; + /* Height of the optical black region for calculation */ + __u16 ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + __u16 ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + __u16 ob_start_v; +}; + +struct isif_black_clamp { + /* + * This offset value is added irrespective of the clamp enable status. + * S13 + */ + __u16 dc_offset; + /* + * Enable black/digital clamp value to be subtracted from the image data + */ + __u8 en; + /* + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + __u8 bc_mode_color; + /* Vertical start position for bc subtraction */ + __u16 vert_start_sub; + /* Black clamp for horizontal direction */ + struct isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +#define ISIF_CSC_NUM_COEFF 16 +struct isif_color_space_conv { + /* Enable color space conversion */ + __u8 en; + /* + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct isif_float_8 coeff[ISIF_CSC_NUM_COEFF]; +}; + + +/************************************************************************* +** Black Compensation parameters +*************************************************************************/ +struct isif_black_comp { + /* Comp for Red */ + __s8 r_comp; + /* Comp for Gr */ + __s8 gr_comp; + /* Comp for Blue */ + __s8 b_comp; + /* Comp for Gb */ + __s8 gb_comp; +}; + +/************************************************************************* +** Gain parameters +*************************************************************************/ +struct isif_gain { + /* Gain for Red or ye */ + struct isif_float_16 r_ye; + /* Gain for Gr or cy */ + struct isif_float_16 gr_cy; + /* Gain for Gb or g */ + struct isif_float_16 gb_g; + /* Gain for Blue or mg */ + struct isif_float_16 b_mg; +}; + +#define ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +struct isif_linearize { + /* Enable or Disable linearization of data */ + __u8 en; + /* Shift value applied */ + __u8 corr_shft; + /* scale factor applied U11Q10 */ + struct isif_float_16 scale_fact; + /* Size of the linear table */ + __u16 table[ISIF_LINEAR_TAB_SIZE]; +}; + +/* Color patterns */ +#define ISIF_RED 0 +#define ISIF_GREEN_RED 1 +#define ISIF_GREEN_BLUE 2 +#define ISIF_BLUE 3 +struct isif_col_pat { + __u8 olop; + __u8 olep; + __u8 elop; + __u8 elep; +}; + +/************************************************************************* +** Data formatter parameters +*************************************************************************/ +struct isif_fmtplen { + /* + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen0; + /* + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen3; +}; + +struct isif_fmt_cfg { +#define ISIF_SPLIT 0 +#define ISIF_COMBINE 1 + /* Split or combine or line alternate */ + __u8 fmtmode; + /* enable or disable line alternating mode */ + __u8 ln_alter_en; +#define ISIF_1LINE 0 +#define ISIF_2LINES 1 +#define ISIF_3LINES 2 +#define ISIF_4LINES 3 + /* Split/combine line number */ + __u8 lnum; + /* Address increment Range 1 - 16 */ + __u8 addrinc; +}; + +struct isif_fmt_addr_ptr { + /* Initial address */ + __u32 init_addr; + /* output line number */ +#define ISIF_1STLINE 0 +#define ISIF_2NDLINE 1 +#define ISIF_3RDLINE 2 +#define ISIF_4THLINE 3 + __u8 out_line; +}; + +struct isif_fmtpgm_ap { + /* program address pointer */ + __u8 pgm_aptr; + /* program address increment or decrement */ + __u8 pgmupdt; +}; + +struct isif_data_formatter { + /* Enable/Disable data formatter */ + __u8 en; + /* data formatter configuration */ + struct isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + __u16 fmtrlen; + /* HD interval for output line. Only valid when split line */ + __u16 fmthcnt; + /* formatter address pointers */ + struct isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + __u8 pgm_en[32]; + /* program address pointers */ + struct isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + __u8 df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct isif_data_formatter df; + /* start pixel in a line at the input */ + __u32 start_pix; + /* number of pixels in input line */ + __u32 num_pixels; + /* start line at the input */ + __u32 start_line; + /* number of lines at the input */ + __u32 num_lines; +}; + +struct isif_gain_offsets_adj { + /* Gain adjustment per color */ + struct isif_gain gain; + /* Offset adjustment */ + __u16 offset; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 offset_h3a_en; +}; + +struct isif_cul { + /* Horizontal Cull pattern for odd lines */ + __u8 hcpat_odd; + /* Horizontal Cull pattern for even lines */ + __u8 hcpat_even; + /* Vertical Cull pattern */ + __u8 vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + __u8 en_lpf; +}; + +struct isif_compress { +#define ISIF_ALAW 0 +#define ISIF_DPCM 1 +#define ISIF_NO_COMPRESSION 2 + /* Compression Algorithm used */ + __u8 alg; + /* Choose Predictor1 for DPCM compression */ +#define ISIF_DPCM_PRED1 0 + /* Choose Predictor2 for DPCM compression */ +#define ISIF_DPCM_PRED2 1 + /* Predictor for DPCM compression */ + __u8 pred; +}; + +/* all the stuff in this struct will be provided by userland */ +struct isif_config_params_raw { + /* Linearization parameters for image sensor data input */ + struct isif_linearize linearize; + /* Data formatter or CSC */ + struct isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) configuration */ + struct isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct isif_gain_offsets_adj gain_offset; + /* Culling */ + struct isif_cul culling; + /* A-Law and DPCM compression options */ + struct isif_compress compress; + /* horizontal offset for Gain/LSC/DFC */ + __u16 horz_offset; + /* vertical offset for Gain/LSC/DFC */ + __u16 vert_offset; + /* color pattern for field 0 */ + struct isif_col_pat col_pat_field0; + /* color pattern for field 1 */ + struct isif_col_pat col_pat_field1; +#define ISIF_NO_SHIFT 0 +#define ISIF_1BIT_SHIFT 1 +#define ISIF_2BIT_SHIFT 2 +#define ISIF_3BIT_SHIFT 3 +#define ISIF_4BIT_SHIFT 4 +#define ISIF_5BIT_SHIFT 5 +#define ISIF_6BIT_SHIFT 6 + /* Data shift applied before storing to SDRAM */ + __u8 data_shift; + /* enable input test pattern generation */ + __u8 test_pat_gen; +}; + +#ifdef __KERNEL__ +struct isif_ycbcr_config { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* ISIF crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum ccdc_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum ccdc_buftype buf_type; +}; + +/* MSB of image data connected to sensor port */ +enum isif_data_msb { + ISIF_BIT_MSB_15, + ISIF_BIT_MSB_14, + ISIF_BIT_MSB_13, + ISIF_BIT_MSB_12, + ISIF_BIT_MSB_11, + ISIF_BIT_MSB_10, + ISIF_BIT_MSB_9, + ISIF_BIT_MSB_8, + ISIF_BIT_MSB_7 +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC, + ISIF_CFA_PAT_STRIPE +}; + +struct isif_params_raw { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct isif_gain gain; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + + /* all the userland defined stuff*/ + struct isif_config_params_raw config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT, + ISIF_PACK_12BIT, + ISIF_PACK_8BIT +}; + +#define ISIF_WIN_NTSC {0, 0, 720, 480} +#define ISIF_WIN_VGA {0, 0, 640, 480} + +#endif +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h b/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h new file mode 100644 index 000000000000..d68d38841ae7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + */ +#ifndef _ISIF_REGS_H +#define _ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7FFF +#define NUM_PX_HOR_MASK 0x7FFF +#define START_VER_ONE_MASK 0x7FFF +#define START_VER_TWO_MASK 0x7FFF +#define NUM_LINES_VER 0x7FFF + +/* gain - offset masks */ +#define GAIN_INTEGER_SHIFT 9 +#define OFFSET_MASK 0xFFF +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 3 +#define ISIF_DATA_PACK16 0 +#define ISIF_DATA_PACK12 1 +#define ISIF_DATA_PACK8 2 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_SHIFT 12 +#define ISIF_RAW_INPUT_MODE 0 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMMA_WD_MASK 0xF +#define ISIF_ALAW_GAMMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 1 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 1 +#define ISIF_HSIZE_FLIP_SHIFT 12 + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 + +/* Black clamp related */ +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_POS_MASK 0x1FFF +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1FFF +#define ISIF_DF_CSC_LNH_MASK 0x1FFF +#define ISIF_DF_CSC_SLV_MASK 0x1FFF +#define ISIF_DF_CSC_LNV_MASK 0x1FFF +#define ISIF_DF_NUMLINES 0x7FFF +#define ISIF_DF_NUMPIX 0x1FFF + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1FFF +#define ISIF_DATA_V_OFFSET_MASK 0x1FFF + +/* Linearization */ +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 + + +/* Pattern registers */ +#define ISIF_PG_EN (1 << 3) +#define ISIF_SEL_PG_SRC (3 << 4) +#define ISIF_PG_VD_POL_SHIFT 0 +#define ISIF_PG_HD_POL_SHIFT 1 + +/*random other junk*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c b/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c new file mode 100644 index 000000000000..0a2226b321d7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c @@ -0,0 +1,1902 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for Raw Bayer RGB capture + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for USERPTR IO + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 3; +static u32 bufsize = (720 * 576 * 2); + +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 3, + .numbuffers = 3, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static const struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +static const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .bpp = 1, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR16, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + .bpp = 1, + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_YUYV, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .bpp = 1, + }, +}; + +/* + * vpfe_lookup_pix_format() + * lookup an entry in the vpfe pix format table based on pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + if (!dev->hw_ops.open || + !dev->hw_ops.enable || + !dev->hw_ops.set_hw_if_params || + !dev->hw_ops.configure || + !dev->hw_ops.set_buftype || + !dev->hw_ops.get_buftype || + !dev->hw_ops.enum_pix || + !dev->hw_ops.set_frame_format || + !dev->hw_ops.get_frame_format || + !dev->hw_ops.get_pixel_format || + !dev->hw_ops.set_pixel_format || + !dev->hw_ops.set_image_window || + !dev->hw_ops.get_image_window || + !dev->hw_ops.get_line_length || + !dev->hw_ops.getfid) + return -EINVAL; + + mutex_lock(&ccdc_lock); + if (!ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Probably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -EFAULT; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -EINVAL; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -EINVAL; + goto unlock; + } + + ccdc_dev = dev; +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev) +{ + if (!dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device supports get_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + v4l2_std_id std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; + struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + pix->width = vpfe_dev->crop.width; + pix->height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + pix->field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + pix->pixelformat = V4L2_PIX_FMT_UYVY; + v4l2_fill_mbus_format(mbus_fmt, pix, + MEDIA_BUS_FMT_YUYV10_2X10); + } else { + pix->field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + pix->pixelformat = V4L2_PIX_FMT_SBGGR8; + v4l2_fill_mbus_format(mbus_fmt, pix, + MEDIA_BUS_FMT_SBGGR8_1X8); + } + + /* if sub device supports get_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, pad, get_fmt, NULL, &fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting get_fmt from sub device\n"); + return ret; + } + v4l2_fill_pix_format(pix, mbus_fmt); + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); + pix->sizeimage = pix->bytesperline * pix->height; + + return 0; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + + /* set default standard */ + vpfe_dev->std_index = 0; + + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + vpfe_standards[vpfe_dev->std_index].std_id); + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (!ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; + + /* Clear all VPFE/CCDC interrupts */ + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(-1); + +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + v4l2_fh_init(&fh->fh, vdev); + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + v4l2_fh_exit(&fh->fh); + kfree(fh); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + v4l2_fh_add(&fh->fh); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + 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); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + goto clear_intr; + + /* only for 6446 this will be applicable */ + if (ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + goto clear_intr; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe_dev); + goto clear_intr; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } +clear_intr: + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + 0, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + videobuf_stop(&vpfe_dev->buffer_queue); + videobuf_mmap_free(&vpfe_dev->buffer_queue); + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static __poll_t vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (!vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height = %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + strscpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strscpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return 0; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (pix_fmt) { + fmt->pixelformat = pix_fmt->pixelformat; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + if (!pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe_dev); + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (!pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -1; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index ; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + *inp = sdinfo->inputs[index]; + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_subdev *sd; + struct vpfe_subdev_info *sdinfo; + int subdev_index, inp_index; + struct vpfe_route *route; + u32 input, output; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &inp_index, + index); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + sd = vpfe_dev->sd[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + } else { + input = 0; + output = 0; + } + + if (sd) + ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); + + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + vpfe_dev->current_subdev = sdinfo; + if (sd) + vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler; + vpfe_dev->current_input = index; + vpfe_dev->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + ret = vpfe_config_image_format(vpfe_dev, + vpfe_standards[vpfe_dev->std_index].std_id); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_std, std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + *size = vpfe_dev->fmt.fmt.pix.sizeimage; + if (vpfe_dev->memory == V4L2_MEMORY_MMAP && + vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; + } + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static const struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + vpfe_dev->pdev, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_pixelaspect(struct file *file, void *priv, + int type, struct v4l2_fract *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_pixelaspect\n"); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + /* If std_index is invalid, then just return (== 1:1 aspect) */ + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + return 0; + + *f = vpfe_standards[vpfe_dev->std_index].pixelaspect; + return 0; +} + +static int vpfe_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vpfe_dev->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.width = vpfe_standards[vpfe_dev->std_index].width; + sel->r.height = vpfe_standards[vpfe_dev->std_index].height; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vpfe_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_rect rect = sel->r; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (rect.top < 0 || rect.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundary */ + rect.width = ((rect.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((rect.left + rect.width > + vpfe_dev->std_info.active_pixels) || + (rect.top + rect.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_SELECTION params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&rect); + vpfe_dev->fmt.fmt.pix.width = rect.width; + vpfe_dev->fmt.fmt.pix.height = rect.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = rect; + sel->r = rect; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_g_pixelaspect = vpfe_g_pixelaspect, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return -ENOMEM; + } + + vpfe_dev->pdev = &pdev->dev; + + if (!pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENODEV; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (!vpfe_cfg->ccdc || !vpfe_cfg->card_name || !vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(*ccdc_cfg), GFP_KERNEL); + if (!ccdc_cfg) { + ret = -ENOMEM; + goto probe_free_dev_mem; + } + + mutex_lock(&ccdc_lock); + + strscpy(ccdc_cfg->name, vpfe_cfg->ccdc, sizeof(ccdc_cfg->name)); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_free_ccdc_cfg_mem; + } + + vfd = &vpfe_dev->video_dev; + /* Initialize field of video device */ + vfd->release = video_device_release_empty; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->tvnorms = 0; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_release_irq; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%p\n", &vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(&vpfe_dev->video_dev, + VFL_TYPE_VIDEO, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc_array(num_subdevs, + sizeof(*vpfe_dev->sd), + GFP_KERNEL); + if (!vpfe_dev->sd) { + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->name); + ret = -ENXIO; + goto probe_sd_out; + } + } + + /* set first sub device as current one */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler; + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(&vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_free_ccdc_cfg_mem: + kfree(ccdc_cfg); + mutex_unlock(&ccdc_lock); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(&vpfe_dev->video_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int vpfe_suspend(struct device *dev) +{ + return 0; +} + +static int vpfe_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +module_platform_driver(vpfe_driver); diff --git a/include/media/davinci/dm355_ccdc.h b/include/media/davinci/dm355_ccdc.h deleted file mode 100644 index 1f3d00aa46d1..000000000000 --- a/include/media/davinci/dm355_ccdc.h +++ /dev/null @@ -1,308 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005-2009 Texas Instruments Inc - */ -#ifndef _DM355_CCDC_H -#define _DM355_CCDC_H -#include -#include - -/* enum for No of pixel per line to be avg. in Black Clamping */ -enum ccdc_sample_length { - CCDC_SAMPLE_1PIXELS, - CCDC_SAMPLE_2PIXELS, - CCDC_SAMPLE_4PIXELS, - CCDC_SAMPLE_8PIXELS, - CCDC_SAMPLE_16PIXELS -}; - -/* enum for No of lines in Black Clamping */ -enum ccdc_sample_line { - CCDC_SAMPLE_1LINES, - CCDC_SAMPLE_2LINES, - CCDC_SAMPLE_4LINES, - CCDC_SAMPLE_8LINES, - CCDC_SAMPLE_16LINES -}; - -/* enum for Alaw gamma width */ -enum ccdc_gamma_width { - CCDC_GAMMA_BITS_13_4, - CCDC_GAMMA_BITS_12_3, - CCDC_GAMMA_BITS_11_2, - CCDC_GAMMA_BITS_10_1, - CCDC_GAMMA_BITS_09_0 -}; - -enum ccdc_colpats { - CCDC_RED, - CCDC_GREEN_RED, - CCDC_GREEN_BLUE, - CCDC_BLUE -}; - -struct ccdc_col_pat { - enum ccdc_colpats olop; - enum ccdc_colpats olep; - enum ccdc_colpats elop; - enum ccdc_colpats elep; -}; - -enum ccdc_datasft { - CCDC_DATA_NO_SHIFT, - CCDC_DATA_SHIFT_1BIT, - CCDC_DATA_SHIFT_2BIT, - CCDC_DATA_SHIFT_3BIT, - CCDC_DATA_SHIFT_4BIT, - CCDC_DATA_SHIFT_5BIT, - CCDC_DATA_SHIFT_6BIT -}; - -enum ccdc_data_size { - CCDC_DATA_16BITS, - CCDC_DATA_15BITS, - CCDC_DATA_14BITS, - CCDC_DATA_13BITS, - CCDC_DATA_12BITS, - CCDC_DATA_11BITS, - CCDC_DATA_10BITS, - CCDC_DATA_8BITS -}; -enum ccdc_mfilt1 { - CCDC_NO_MEDIAN_FILTER1, - CCDC_AVERAGE_FILTER1, - CCDC_MEDIAN_FILTER1 -}; - -enum ccdc_mfilt2 { - CCDC_NO_MEDIAN_FILTER2, - CCDC_AVERAGE_FILTER2, - CCDC_MEDIAN_FILTER2 -}; - -/* structure for ALaw */ -struct ccdc_a_law { - /* Enable/disable A-Law */ - unsigned char enable; - /* Gamma Width Input */ - enum ccdc_gamma_width gamma_wd; -}; - -/* structure for Black Clamping */ -struct ccdc_black_clamp { - /* only if bClampEnable is TRUE */ - unsigned char b_clamp_enable; - /* only if bClampEnable is TRUE */ - enum ccdc_sample_length sample_pixel; - /* only if bClampEnable is TRUE */ - enum ccdc_sample_line sample_ln; - /* only if bClampEnable is TRUE */ - unsigned short start_pixel; - /* only if bClampEnable is FALSE */ - unsigned short sgain; - unsigned short dc_sub; -}; - -/* structure for Black Level Compensation */ -struct ccdc_black_compensation { - /* Constant value to subtract from Red component */ - unsigned char r; - /* Constant value to subtract from Gr component */ - unsigned char gr; - /* Constant value to subtract from Blue component */ - unsigned char b; - /* Constant value to subtract from Gb component */ - unsigned char gb; -}; - -struct ccdc_float { - int integer; - unsigned int decimal; -}; - -#define CCDC_CSC_COEFF_TABLE_SIZE 16 -/* structure for color space converter */ -struct ccdc_csc { - unsigned char enable; - /* - * S8Q5. Use 2 decimal precision, user values range from -3.00 to 3.99. - * example - to use 1.03, set integer part as 1, and decimal part as 3 - * to use -1.03, set integer part as -1 and decimal part as 3 - */ - struct ccdc_float coeff[CCDC_CSC_COEFF_TABLE_SIZE]; -}; - -/* Structures for Vertical Defect Correction*/ -enum ccdc_vdf_csl { - CCDC_VDF_NORMAL, - CCDC_VDF_HORZ_INTERPOL_SAT, - CCDC_VDF_HORZ_INTERPOL -}; - -enum ccdc_vdf_cuda { - CCDC_VDF_WHOLE_LINE_CORRECT, - CCDC_VDF_UPPER_DISABLE -}; - -enum ccdc_dfc_mwr { - CCDC_DFC_MWR_WRITE_COMPLETE, - CCDC_DFC_WRITE_REG -}; - -enum ccdc_dfc_mrd { - CCDC_DFC_READ_COMPLETE, - CCDC_DFC_READ_REG -}; - -enum ccdc_dfc_ma_rst { - CCDC_DFC_INCR_ADDR, - CCDC_DFC_CLR_ADDR -}; - -enum ccdc_dfc_mclr { - CCDC_DFC_CLEAR_COMPLETE, - CCDC_DFC_CLEAR -}; - -struct ccdc_dft_corr_ctl { - enum ccdc_vdf_csl vdfcsl; - enum ccdc_vdf_cuda vdfcuda; - unsigned int vdflsft; -}; - -struct ccdc_dft_corr_mem_ctl { - enum ccdc_dfc_mwr dfcmwr; - enum ccdc_dfc_mrd dfcmrd; - enum ccdc_dfc_ma_rst dfcmarst; - enum ccdc_dfc_mclr dfcmclr; -}; - -#define CCDC_DFT_TABLE_SIZE 16 -/* - * Main Structure for vertical defect correction. Vertical defect - * correction can correct up to 16 defects if defects less than 16 - * then pad the rest with 0 - */ -struct ccdc_vertical_dft { - unsigned char ver_dft_en; - unsigned char gen_dft_en; - unsigned int saturation_ctl; - struct ccdc_dft_corr_ctl dft_corr_ctl; - struct ccdc_dft_corr_mem_ctl dft_corr_mem_ctl; - int table_size; - unsigned int dft_corr_horz[CCDC_DFT_TABLE_SIZE]; - unsigned int dft_corr_vert[CCDC_DFT_TABLE_SIZE]; - unsigned int dft_corr_sub1[CCDC_DFT_TABLE_SIZE]; - unsigned int dft_corr_sub2[CCDC_DFT_TABLE_SIZE]; - unsigned int dft_corr_sub3[CCDC_DFT_TABLE_SIZE]; -}; - -struct ccdc_data_offset { - unsigned char horz_offset; - unsigned char vert_offset; -}; - -/* - * Structure for CCDC configuration parameters for raw capture mode passed - * by application - */ -struct ccdc_config_params_raw { - /* data shift to be applied before storing */ - enum ccdc_datasft datasft; - /* data size value from 8 to 16 bits */ - enum ccdc_data_size data_sz; - /* median filter for sdram */ - enum ccdc_mfilt1 mfilt1; - enum ccdc_mfilt2 mfilt2; - /* low pass filter enable/disable */ - unsigned char lpf_enable; - /* Threshold of median filter */ - int med_filt_thres; - /* - * horz and vertical data offset. Applicable for defect correction - * and lsc - */ - struct ccdc_data_offset data_offset; - /* Structure for Optional A-Law */ - struct ccdc_a_law alaw; - /* Structure for Optical Black Clamp */ - struct ccdc_black_clamp blk_clamp; - /* Structure for Black Compensation */ - struct ccdc_black_compensation blk_comp; - /* structure for vertical Defect Correction Module Configuration */ - struct ccdc_vertical_dft vertical_dft; - /* structure for color space converter Module Configuration */ - struct ccdc_csc csc; - /* color patters for bayer capture */ - struct ccdc_col_pat col_pat_field0; - struct ccdc_col_pat col_pat_field1; -}; - -#ifdef __KERNEL__ -#include - -#define CCDC_WIN_PAL {0, 0, 720, 576} -#define CCDC_WIN_VGA {0, 0, 640, 480} - -struct ccdc_params_ycbcr { - /* pixel format */ - enum ccdc_pixfmt pix_fmt; - /* progressive or interlaced frame */ - enum ccdc_frmfmt frm_fmt; - /* video window */ - struct v4l2_rect win; - /* field id polarity */ - enum vpfe_pin_pol fid_pol; - /* vertical sync polarity */ - enum vpfe_pin_pol vd_pol; - /* horizontal sync polarity */ - enum vpfe_pin_pol hd_pol; - /* enable BT.656 embedded sync mode */ - int bt656_enable; - /* cb:y:cr:y or y:cb:y:cr in memory */ - enum ccdc_pixorder pix_order; - /* interleaved or separated fields */ - enum ccdc_buftype buf_type; -}; - -/* Gain applied to Raw Bayer data */ -struct ccdc_gain { - unsigned short r_ye; - unsigned short gr_cy; - unsigned short gb_g; - unsigned short b_mg; -}; - -/* Structure for CCDC configuration parameters for raw capture mode */ -struct ccdc_params_raw { - /* pixel format */ - enum ccdc_pixfmt pix_fmt; - /* progressive or interlaced frame */ - enum ccdc_frmfmt frm_fmt; - /* video window */ - struct v4l2_rect win; - /* field id polarity */ - enum vpfe_pin_pol fid_pol; - /* vertical sync polarity */ - enum vpfe_pin_pol vd_pol; - /* horizontal sync polarity */ - enum vpfe_pin_pol hd_pol; - /* interleaved or separated fields */ - enum ccdc_buftype buf_type; - /* Gain values */ - struct ccdc_gain gain; - /* offset */ - unsigned int ccdc_offset; - /* horizontal flip enable */ - unsigned char horz_flip_enable; - /* - * enable to store the image in inverse order in memory - * (bottom to top) - */ - unsigned char image_invert_enable; - /* Configurable part of raw data */ - struct ccdc_config_params_raw config_params; -}; - -#endif -#endif /* DM355_CCDC_H */ diff --git a/include/media/davinci/dm644x_ccdc.h b/include/media/davinci/dm644x_ccdc.h deleted file mode 100644 index c20dba3d76d6..000000000000 --- a/include/media/davinci/dm644x_ccdc.h +++ /dev/null @@ -1,171 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2006-2009 Texas Instruments Inc - */ -#ifndef _DM644X_CCDC_H -#define _DM644X_CCDC_H -#include -#include - -/* enum for No of pixel per line to be avg. in Black Clamping*/ -enum ccdc_sample_length { - CCDC_SAMPLE_1PIXELS, - CCDC_SAMPLE_2PIXELS, - CCDC_SAMPLE_4PIXELS, - CCDC_SAMPLE_8PIXELS, - CCDC_SAMPLE_16PIXELS -}; - -/* enum for No of lines in Black Clamping */ -enum ccdc_sample_line { - CCDC_SAMPLE_1LINES, - CCDC_SAMPLE_2LINES, - CCDC_SAMPLE_4LINES, - CCDC_SAMPLE_8LINES, - CCDC_SAMPLE_16LINES -}; - -/* enum for Alaw gamma width */ -enum ccdc_gamma_width { - CCDC_GAMMA_BITS_15_6, /* use bits 15-6 for gamma */ - CCDC_GAMMA_BITS_14_5, - CCDC_GAMMA_BITS_13_4, - CCDC_GAMMA_BITS_12_3, - CCDC_GAMMA_BITS_11_2, - CCDC_GAMMA_BITS_10_1, - CCDC_GAMMA_BITS_09_0 /* use bits 9-0 for gamma */ -}; - -/* returns the highest bit used for the gamma */ -static inline u8 ccdc_gamma_width_max_bit(enum ccdc_gamma_width width) -{ - return 15 - width; -} - -enum ccdc_data_size { - CCDC_DATA_16BITS, - CCDC_DATA_15BITS, - CCDC_DATA_14BITS, - CCDC_DATA_13BITS, - CCDC_DATA_12BITS, - CCDC_DATA_11BITS, - CCDC_DATA_10BITS, - CCDC_DATA_8BITS -}; - -/* returns the highest bit used for this data size */ -static inline u8 ccdc_data_size_max_bit(enum ccdc_data_size sz) -{ - return sz == CCDC_DATA_8BITS ? 7 : 15 - sz; -} - -/* structure for ALaw */ -struct ccdc_a_law { - /* Enable/disable A-Law */ - unsigned char enable; - /* Gamma Width Input */ - enum ccdc_gamma_width gamma_wd; -}; - -/* structure for Black Clamping */ -struct ccdc_black_clamp { - unsigned char enable; - /* only if bClampEnable is TRUE */ - enum ccdc_sample_length sample_pixel; - /* only if bClampEnable is TRUE */ - enum ccdc_sample_line sample_ln; - /* only if bClampEnable is TRUE */ - unsigned short start_pixel; - /* only if bClampEnable is TRUE */ - unsigned short sgain; - /* only if bClampEnable is FALSE */ - unsigned short dc_sub; -}; - -/* structure for Black Level Compensation */ -struct ccdc_black_compensation { - /* Constant value to subtract from Red component */ - char r; - /* Constant value to subtract from Gr component */ - char gr; - /* Constant value to subtract from Blue component */ - char b; - /* Constant value to subtract from Gb component */ - char gb; -}; - -/* Structure for CCDC configuration parameters for raw capture mode passed - * by application - */ -struct ccdc_config_params_raw { - /* data size value from 8 to 16 bits */ - enum ccdc_data_size data_sz; - /* Structure for Optional A-Law */ - struct ccdc_a_law alaw; - /* Structure for Optical Black Clamp */ - struct ccdc_black_clamp blk_clamp; - /* Structure for Black Compensation */ - struct ccdc_black_compensation blk_comp; -}; - - -#ifdef __KERNEL__ -#include -/* Define to enable/disable video port */ -#define FP_NUM_BYTES 4 -/* Define for extra pixel/line and extra lines/frame */ -#define NUM_EXTRAPIXELS 8 -#define NUM_EXTRALINES 8 - -/* settings for commonly used video formats */ -#define CCDC_WIN_PAL {0, 0, 720, 576} -/* ntsc square pixel */ -#define CCDC_WIN_VGA {0, 0, (640 + NUM_EXTRAPIXELS), (480 + NUM_EXTRALINES)} - -/* Structure for CCDC configuration parameters for raw capture mode */ -struct ccdc_params_raw { - /* pixel format */ - enum ccdc_pixfmt pix_fmt; - /* progressive or interlaced frame */ - enum ccdc_frmfmt frm_fmt; - /* video window */ - struct v4l2_rect win; - /* field id polarity */ - enum vpfe_pin_pol fid_pol; - /* vertical sync polarity */ - enum vpfe_pin_pol vd_pol; - /* horizontal sync polarity */ - enum vpfe_pin_pol hd_pol; - /* interleaved or separated fields */ - enum ccdc_buftype buf_type; - /* - * enable to store the image in inverse - * order in memory(bottom to top) - */ - unsigned char image_invert_enable; - /* configurable parameters */ - struct ccdc_config_params_raw config_params; -}; - -struct ccdc_params_ycbcr { - /* pixel format */ - enum ccdc_pixfmt pix_fmt; - /* progressive or interlaced frame */ - enum ccdc_frmfmt frm_fmt; - /* video window */ - struct v4l2_rect win; - /* field id polarity */ - enum vpfe_pin_pol fid_pol; - /* vertical sync polarity */ - enum vpfe_pin_pol vd_pol; - /* horizontal sync polarity */ - enum vpfe_pin_pol hd_pol; - /* enable BT.656 embedded sync mode */ - int bt656_enable; - /* cb:y:cr:y or y:cb:y:cr in memory */ - enum ccdc_pixorder pix_order; - /* interleaved or separated fields */ - enum ccdc_buftype buf_type; -}; -#endif -#endif /* _DM644X_CCDC_H */ diff --git a/include/media/davinci/isif.h b/include/media/davinci/isif.h deleted file mode 100644 index 8369acd26e7e..000000000000 --- a/include/media/davinci/isif.h +++ /dev/null @@ -1,518 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * isif header file - */ -#ifndef _ISIF_H -#define _ISIF_H - -#include -#include - -/* isif float type S8Q8/U8Q8 */ -struct isif_float_8 { - /* 8 bit integer part */ - __u8 integer; - /* 8 bit decimal part */ - __u8 decimal; -}; - -/* isif float type U16Q16/S16Q16 */ -struct isif_float_16 { - /* 16 bit integer part */ - __u16 integer; - /* 16 bit decimal part */ - __u16 decimal; -}; - -/************************************************************************ - * Vertical Defect Correction parameters - ***********************************************************************/ -/* Defect Correction (DFC) table entry */ -struct isif_vdfc_entry { - /* vertical position of defect */ - __u16 pos_vert; - /* horizontal position of defect */ - __u16 pos_horz; - /* - * Defect level of Vertical line defect position. This is subtracted - * from the data at the defect position - */ - __u8 level_at_pos; - /* - * Defect level of the pixels upper than the vertical line defect. - * This is subtracted from the data - */ - __u8 level_up_pixels; - /* - * Defect level of the pixels lower than the vertical line defect. - * This is subtracted from the data - */ - __u8 level_low_pixels; -}; - -#define ISIF_VDFC_TABLE_SIZE 8 -struct isif_dfc { - /* enable vertical defect correction */ - __u8 en; - /* Defect level subtraction. Just fed through if saturating */ -#define ISIF_VDFC_NORMAL 0 - /* - * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 - * if data saturating - */ -#define ISIF_VDFC_HORZ_INTERPOL_IF_SAT 1 - /* Horizontal interpolation (((i-2)+(i+2))/2) */ -#define ISIF_VDFC_HORZ_INTERPOL 2 - /* one of the vertical defect correction modes above */ - __u8 corr_mode; - /* 0 - whole line corrected, 1 - not pixels upper than the defect */ - __u8 corr_whole_line; -#define ISIF_VDFC_NO_SHIFT 0 -#define ISIF_VDFC_SHIFT_1 1 -#define ISIF_VDFC_SHIFT_2 2 -#define ISIF_VDFC_SHIFT_3 3 -#define ISIF_VDFC_SHIFT_4 4 - /* - * defect level shift value. level_at_pos, level_upper_pos, - * and level_lower_pos can be shifted up by this value. Choose - * one of the values above - */ - __u8 def_level_shift; - /* defect saturation level */ - __u16 def_sat_level; - /* number of vertical defects. Max is ISIF_VDFC_TABLE_SIZE */ - __u16 num_vdefects; - /* VDFC table ptr */ - struct isif_vdfc_entry table[ISIF_VDFC_TABLE_SIZE]; -}; - -struct isif_horz_bclamp { - - /* Horizontal clamp disabled. Only vertical clamp value is subtracted */ -#define ISIF_HORZ_BC_DISABLE 0 - /* - * Horizontal clamp value is calculated and subtracted from image data - * along with vertical clamp value - */ -#define ISIF_HORZ_BC_CLAMP_CALC_ENABLED 1 - /* - * Horizontal clamp value calculated from previous image is subtracted - * from image data along with vertical clamp value. - */ -#define ISIF_HORZ_BC_CLAMP_NOT_UPDATED 2 - /* horizontal clamp mode. One of the values above */ - __u8 mode; - /* - * pixel value limit enable. - * 0 - limit disabled - * 1 - pixel value limited to 1023 - */ - __u8 clamp_pix_limit; - /* Select Most left window for bc calculation */ -#define ISIF_SEL_MOST_LEFT_WIN 0 - /* Select Most right window for bc calculation */ -#define ISIF_SEL_MOST_RIGHT_WIN 1 - /* Select most left or right window for clamp val calculation */ - __u8 base_win_sel_calc; - /* Window count per color for calculation. range 1-32 */ - __u8 win_count_calc; - /* Window start position - horizontal for calculation. 0 - 8191 */ - __u16 win_start_h_calc; - /* Window start position - vertical for calculation 0 - 8191 */ - __u16 win_start_v_calc; -#define ISIF_HORZ_BC_SZ_H_2PIXELS 0 -#define ISIF_HORZ_BC_SZ_H_4PIXELS 1 -#define ISIF_HORZ_BC_SZ_H_8PIXELS 2 -#define ISIF_HORZ_BC_SZ_H_16PIXELS 3 - /* Width of the sample window in pixels for calculation */ - __u8 win_h_sz_calc; -#define ISIF_HORZ_BC_SZ_V_32PIXELS 0 -#define ISIF_HORZ_BC_SZ_V_64PIXELS 1 -#define ISIF_HORZ_BC_SZ_V_128PIXELS 2 -#define ISIF_HORZ_BC_SZ_V_256PIXELS 3 - /* Height of the sample window in pixels for calculation */ - __u8 win_v_sz_calc; -}; - -/************************************************************************ - * Black Clamp parameters - ***********************************************************************/ -struct isif_vert_bclamp { - /* Reset value used is the clamp value calculated */ -#define ISIF_VERT_BC_USE_HORZ_CLAMP_VAL 0 - /* Reset value used is reset_clamp_val configured */ -#define ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL 1 - /* No update, previous image value is used */ -#define ISIF_VERT_BC_NO_UPDATE 2 - /* - * Reset value selector for vertical clamp calculation. Use one of - * the above values - */ - __u8 reset_val_sel; - /* U8Q8. Line average coefficient used in vertical clamp calculation */ - __u8 line_ave_coef; - /* Height of the optical black region for calculation */ - __u16 ob_v_sz_calc; - /* Optical black region start position - horizontal. 0 - 8191 */ - __u16 ob_start_h; - /* Optical black region start position - vertical 0 - 8191 */ - __u16 ob_start_v; -}; - -struct isif_black_clamp { - /* - * This offset value is added irrespective of the clamp enable status. - * S13 - */ - __u16 dc_offset; - /* - * Enable black/digital clamp value to be subtracted from the image data - */ - __u8 en; - /* - * black clamp mode. same/separate clamp for 4 colors - * 0 - disable - same clamp value for all colors - * 1 - clamp value calculated separately for all colors - */ - __u8 bc_mode_color; - /* Vertical start position for bc subtraction */ - __u16 vert_start_sub; - /* Black clamp for horizontal direction */ - struct isif_horz_bclamp horz; - /* Black clamp for vertical direction */ - struct isif_vert_bclamp vert; -}; - -/************************************************************************* -** Color Space Conversion (CSC) -*************************************************************************/ -#define ISIF_CSC_NUM_COEFF 16 -struct isif_color_space_conv { - /* Enable color space conversion */ - __u8 en; - /* - * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and - * so forth - */ - struct isif_float_8 coeff[ISIF_CSC_NUM_COEFF]; -}; - - -/************************************************************************* -** Black Compensation parameters -*************************************************************************/ -struct isif_black_comp { - /* Comp for Red */ - __s8 r_comp; - /* Comp for Gr */ - __s8 gr_comp; - /* Comp for Blue */ - __s8 b_comp; - /* Comp for Gb */ - __s8 gb_comp; -}; - -/************************************************************************* -** Gain parameters -*************************************************************************/ -struct isif_gain { - /* Gain for Red or ye */ - struct isif_float_16 r_ye; - /* Gain for Gr or cy */ - struct isif_float_16 gr_cy; - /* Gain for Gb or g */ - struct isif_float_16 gb_g; - /* Gain for Blue or mg */ - struct isif_float_16 b_mg; -}; - -#define ISIF_LINEAR_TAB_SIZE 192 -/************************************************************************* -** Linearization parameters -*************************************************************************/ -struct isif_linearize { - /* Enable or Disable linearization of data */ - __u8 en; - /* Shift value applied */ - __u8 corr_shft; - /* scale factor applied U11Q10 */ - struct isif_float_16 scale_fact; - /* Size of the linear table */ - __u16 table[ISIF_LINEAR_TAB_SIZE]; -}; - -/* Color patterns */ -#define ISIF_RED 0 -#define ISIF_GREEN_RED 1 -#define ISIF_GREEN_BLUE 2 -#define ISIF_BLUE 3 -struct isif_col_pat { - __u8 olop; - __u8 olep; - __u8 elop; - __u8 elep; -}; - -/************************************************************************* -** Data formatter parameters -*************************************************************************/ -struct isif_fmtplen { - /* - * number of program entries for SET0, range 1 - 16 - * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is - * ISIF_COMBINE - */ - __u16 plen0; - /* - * number of program entries for SET1, range 1 - 16 - * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is - * ISIF_COMBINE - */ - __u16 plen1; - /** - * number of program entries for SET2, range 1 - 16 - * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is - * ISIF_COMBINE - */ - __u16 plen2; - /** - * number of program entries for SET3, range 1 - 16 - * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is - * ISIF_COMBINE - */ - __u16 plen3; -}; - -struct isif_fmt_cfg { -#define ISIF_SPLIT 0 -#define ISIF_COMBINE 1 - /* Split or combine or line alternate */ - __u8 fmtmode; - /* enable or disable line alternating mode */ - __u8 ln_alter_en; -#define ISIF_1LINE 0 -#define ISIF_2LINES 1 -#define ISIF_3LINES 2 -#define ISIF_4LINES 3 - /* Split/combine line number */ - __u8 lnum; - /* Address increment Range 1 - 16 */ - __u8 addrinc; -}; - -struct isif_fmt_addr_ptr { - /* Initial address */ - __u32 init_addr; - /* output line number */ -#define ISIF_1STLINE 0 -#define ISIF_2NDLINE 1 -#define ISIF_3RDLINE 2 -#define ISIF_4THLINE 3 - __u8 out_line; -}; - -struct isif_fmtpgm_ap { - /* program address pointer */ - __u8 pgm_aptr; - /* program address increment or decrement */ - __u8 pgmupdt; -}; - -struct isif_data_formatter { - /* Enable/Disable data formatter */ - __u8 en; - /* data formatter configuration */ - struct isif_fmt_cfg cfg; - /* Formatter program entries length */ - struct isif_fmtplen plen; - /* first pixel in a line fed to formatter */ - __u16 fmtrlen; - /* HD interval for output line. Only valid when split line */ - __u16 fmthcnt; - /* formatter address pointers */ - struct isif_fmt_addr_ptr fmtaddr_ptr[16]; - /* program enable/disable */ - __u8 pgm_en[32]; - /* program address pointers */ - struct isif_fmtpgm_ap fmtpgm_ap[32]; -}; - -struct isif_df_csc { - /* Color Space Conversion configuration, 0 - csc, 1 - df */ - __u8 df_or_csc; - /* csc configuration valid if df_or_csc is 0 */ - struct isif_color_space_conv csc; - /* data formatter configuration valid if df_or_csc is 1 */ - struct isif_data_formatter df; - /* start pixel in a line at the input */ - __u32 start_pix; - /* number of pixels in input line */ - __u32 num_pixels; - /* start line at the input */ - __u32 start_line; - /* number of lines at the input */ - __u32 num_lines; -}; - -struct isif_gain_offsets_adj { - /* Gain adjustment per color */ - struct isif_gain gain; - /* Offset adjustment */ - __u16 offset; - /* Enable or Disable Gain adjustment for SDRAM data */ - __u8 gain_sdram_en; - /* Enable or Disable Gain adjustment for IPIPE data */ - __u8 gain_ipipe_en; - /* Enable or Disable Gain adjustment for H3A data */ - __u8 gain_h3a_en; - /* Enable or Disable Gain adjustment for SDRAM data */ - __u8 offset_sdram_en; - /* Enable or Disable Gain adjustment for IPIPE data */ - __u8 offset_ipipe_en; - /* Enable or Disable Gain adjustment for H3A data */ - __u8 offset_h3a_en; -}; - -struct isif_cul { - /* Horizontal Cull pattern for odd lines */ - __u8 hcpat_odd; - /* Horizontal Cull pattern for even lines */ - __u8 hcpat_even; - /* Vertical Cull pattern */ - __u8 vcpat; - /* Enable or disable lpf. Apply when cull is enabled */ - __u8 en_lpf; -}; - -struct isif_compress { -#define ISIF_ALAW 0 -#define ISIF_DPCM 1 -#define ISIF_NO_COMPRESSION 2 - /* Compression Algorithm used */ - __u8 alg; - /* Choose Predictor1 for DPCM compression */ -#define ISIF_DPCM_PRED1 0 - /* Choose Predictor2 for DPCM compression */ -#define ISIF_DPCM_PRED2 1 - /* Predictor for DPCM compression */ - __u8 pred; -}; - -/* all the stuff in this struct will be provided by userland */ -struct isif_config_params_raw { - /* Linearization parameters for image sensor data input */ - struct isif_linearize linearize; - /* Data formatter or CSC */ - struct isif_df_csc df_csc; - /* Defect Pixel Correction (DFC) configuration */ - struct isif_dfc dfc; - /* Black/Digital Clamp configuration */ - struct isif_black_clamp bclamp; - /* Gain, offset adjustments */ - struct isif_gain_offsets_adj gain_offset; - /* Culling */ - struct isif_cul culling; - /* A-Law and DPCM compression options */ - struct isif_compress compress; - /* horizontal offset for Gain/LSC/DFC */ - __u16 horz_offset; - /* vertical offset for Gain/LSC/DFC */ - __u16 vert_offset; - /* color pattern for field 0 */ - struct isif_col_pat col_pat_field0; - /* color pattern for field 1 */ - struct isif_col_pat col_pat_field1; -#define ISIF_NO_SHIFT 0 -#define ISIF_1BIT_SHIFT 1 -#define ISIF_2BIT_SHIFT 2 -#define ISIF_3BIT_SHIFT 3 -#define ISIF_4BIT_SHIFT 4 -#define ISIF_5BIT_SHIFT 5 -#define ISIF_6BIT_SHIFT 6 - /* Data shift applied before storing to SDRAM */ - __u8 data_shift; - /* enable input test pattern generation */ - __u8 test_pat_gen; -}; - -#ifdef __KERNEL__ -struct isif_ycbcr_config { - /* isif pixel format */ - enum ccdc_pixfmt pix_fmt; - /* isif frame format */ - enum ccdc_frmfmt frm_fmt; - /* ISIF crop window */ - struct v4l2_rect win; - /* field polarity */ - enum vpfe_pin_pol fid_pol; - /* interface VD polarity */ - enum vpfe_pin_pol vd_pol; - /* interface HD polarity */ - enum vpfe_pin_pol hd_pol; - /* isif pix order. Only used for ycbcr capture */ - enum ccdc_pixorder pix_order; - /* isif buffer type. Only used for ycbcr capture */ - enum ccdc_buftype buf_type; -}; - -/* MSB of image data connected to sensor port */ -enum isif_data_msb { - ISIF_BIT_MSB_15, - ISIF_BIT_MSB_14, - ISIF_BIT_MSB_13, - ISIF_BIT_MSB_12, - ISIF_BIT_MSB_11, - ISIF_BIT_MSB_10, - ISIF_BIT_MSB_9, - ISIF_BIT_MSB_8, - ISIF_BIT_MSB_7 -}; - -enum isif_cfa_pattern { - ISIF_CFA_PAT_MOSAIC, - ISIF_CFA_PAT_STRIPE -}; - -struct isif_params_raw { - /* isif pixel format */ - enum ccdc_pixfmt pix_fmt; - /* isif frame format */ - enum ccdc_frmfmt frm_fmt; - /* video window */ - struct v4l2_rect win; - /* field polarity */ - enum vpfe_pin_pol fid_pol; - /* interface VD polarity */ - enum vpfe_pin_pol vd_pol; - /* interface HD polarity */ - enum vpfe_pin_pol hd_pol; - /* buffer type. Applicable for interlaced mode */ - enum ccdc_buftype buf_type; - /* Gain values */ - struct isif_gain gain; - /* cfa pattern */ - enum isif_cfa_pattern cfa_pat; - /* Data MSB position */ - enum isif_data_msb data_msb; - /* Enable horizontal flip */ - unsigned char horz_flip_en; - /* Enable image invert vertically */ - unsigned char image_invert_en; - - /* all the userland defined stuff*/ - struct isif_config_params_raw config_params; -}; - -enum isif_data_pack { - ISIF_PACK_16BIT, - ISIF_PACK_12BIT, - ISIF_PACK_8BIT -}; - -#define ISIF_WIN_NTSC {0, 0, 720, 480} -#define ISIF_WIN_VGA {0, 0, 640, 480} - -#endif -#endif -- cgit v1.3-14-g43fede From e33fdb5a02490059e2f48ced2c038c8a46c6476d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:48 +0200 Subject: media: saa7146: deprecate hexium_gemini/orion, mxb and ttpci Deprecate the hexium_gemini, hexium_orion, mxb and ttpci saa7146-based drivers: these drivers do not use the vb2 framework for video streaming, instead it uses the old videobuf framework. We want to get rid of these old drivers, so deprecated these for future removal. [hverkuil: update MAINTAINERS file] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 3 +- drivers/media/common/Kconfig | 1 - drivers/media/common/Makefile | 2 +- drivers/media/common/saa7146/Kconfig | 10 - drivers/media/common/saa7146/Makefile | 6 - drivers/media/common/saa7146/saa7146_core.c | 578 ------- drivers/media/common/saa7146/saa7146_fops.c | 658 -------- drivers/media/common/saa7146/saa7146_hlp.c | 1046 ------------- drivers/media/common/saa7146/saa7146_i2c.c | 421 ----- drivers/media/common/saa7146/saa7146_vbi.c | 498 ------ drivers/media/common/saa7146/saa7146_video.c | 1286 ---------------- drivers/media/pci/Kconfig | 2 - drivers/media/pci/Makefile | 4 +- drivers/media/pci/saa7146/Kconfig | 39 - drivers/media/pci/saa7146/Makefile | 6 - drivers/media/pci/saa7146/hexium_gemini.c | 425 ----- drivers/media/pci/saa7146/hexium_orion.c | 496 ------ drivers/media/pci/saa7146/mxb.c | 873 ----------- drivers/media/pci/ttpci/Kconfig | 86 -- drivers/media/pci/ttpci/Makefile | 13 - drivers/media/pci/ttpci/budget-av.c | 1622 -------------------- drivers/media/pci/ttpci/budget-ci.c | 1574 ------------------- drivers/media/pci/ttpci/budget-core.c | 603 -------- drivers/media/pci/ttpci/budget.c | 883 ----------- drivers/media/pci/ttpci/budget.h | 129 -- drivers/staging/media/Kconfig | 1 + drivers/staging/media/Makefile | 1 + drivers/staging/media/av7110/Makefile | 3 +- drivers/staging/media/av7110/av7110.h | 2 +- drivers/staging/media/deprecated/saa7146/Kconfig | 4 + drivers/staging/media/deprecated/saa7146/Makefile | 2 + .../media/deprecated/saa7146/common/Kconfig | 10 + .../media/deprecated/saa7146/common/Makefile | 6 + .../media/deprecated/saa7146/common/saa7146.h | 472 ++++++ .../media/deprecated/saa7146/common/saa7146_core.c | 578 +++++++ .../media/deprecated/saa7146/common/saa7146_fops.c | 658 ++++++++ .../media/deprecated/saa7146/common/saa7146_hlp.c | 1046 +++++++++++++ .../media/deprecated/saa7146/common/saa7146_i2c.c | 421 +++++ .../media/deprecated/saa7146/common/saa7146_vbi.c | 498 ++++++ .../deprecated/saa7146/common/saa7146_video.c | 1286 ++++++++++++++++ .../media/deprecated/saa7146/common/saa7146_vv.h | 266 ++++ .../media/deprecated/saa7146/saa7146/Kconfig | 48 + .../media/deprecated/saa7146/saa7146/Makefile | 6 + .../staging/media/deprecated/saa7146/saa7146/TODO | 7 + .../deprecated/saa7146/saa7146/hexium_gemini.c | 425 +++++ .../deprecated/saa7146/saa7146/hexium_orion.c | 496 ++++++ .../staging/media/deprecated/saa7146/saa7146/mxb.c | 873 +++++++++++ .../staging/media/deprecated/saa7146/ttpci/Kconfig | 95 ++ .../media/deprecated/saa7146/ttpci/Makefile | 13 + .../staging/media/deprecated/saa7146/ttpci/TODO | 7 + .../media/deprecated/saa7146/ttpci/budget-av.c | 1622 ++++++++++++++++++++ .../media/deprecated/saa7146/ttpci/budget-ci.c | 1574 +++++++++++++++++++ .../media/deprecated/saa7146/ttpci/budget-core.c | 603 ++++++++ .../media/deprecated/saa7146/ttpci/budget.c | 883 +++++++++++ .../media/deprecated/saa7146/ttpci/budget.h | 129 ++ include/media/drv-intf/saa7146.h | 472 ------ include/media/drv-intf/saa7146_vv.h | 266 ---- 57 files changed, 12036 insertions(+), 12001 deletions(-) delete mode 100644 drivers/media/common/saa7146/Kconfig delete mode 100644 drivers/media/common/saa7146/Makefile delete mode 100644 drivers/media/common/saa7146/saa7146_core.c delete mode 100644 drivers/media/common/saa7146/saa7146_fops.c delete mode 100644 drivers/media/common/saa7146/saa7146_hlp.c delete mode 100644 drivers/media/common/saa7146/saa7146_i2c.c delete mode 100644 drivers/media/common/saa7146/saa7146_vbi.c delete mode 100644 drivers/media/common/saa7146/saa7146_video.c delete mode 100644 drivers/media/pci/saa7146/Kconfig delete mode 100644 drivers/media/pci/saa7146/Makefile delete mode 100644 drivers/media/pci/saa7146/hexium_gemini.c delete mode 100644 drivers/media/pci/saa7146/hexium_orion.c delete mode 100644 drivers/media/pci/saa7146/mxb.c delete mode 100644 drivers/media/pci/ttpci/Kconfig delete mode 100644 drivers/media/pci/ttpci/Makefile delete mode 100644 drivers/media/pci/ttpci/budget-av.c delete mode 100644 drivers/media/pci/ttpci/budget-ci.c delete mode 100644 drivers/media/pci/ttpci/budget-core.c delete mode 100644 drivers/media/pci/ttpci/budget.c delete mode 100644 drivers/media/pci/ttpci/budget.h create mode 100644 drivers/staging/media/deprecated/saa7146/Kconfig create mode 100644 drivers/staging/media/deprecated/saa7146/Makefile create mode 100644 drivers/staging/media/deprecated/saa7146/common/Kconfig create mode 100644 drivers/staging/media/deprecated/saa7146/common/Makefile create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146.h create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_core.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_video.c create mode 100644 drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/Kconfig create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/Makefile create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/TODO create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c create mode 100644 drivers/staging/media/deprecated/saa7146/saa7146/mxb.c create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/Kconfig create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/Makefile create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/TODO create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/budget.c create mode 100644 drivers/staging/media/deprecated/saa7146/ttpci/budget.h delete mode 100644 include/media/drv-intf/saa7146.h delete mode 100644 include/media/drv-intf/saa7146_vv.h diff --git a/MAINTAINERS b/MAINTAINERS index 7f123e4d0f0a..d457299ce736 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17910,8 +17910,7 @@ M: Hans Verkuil L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: drivers/media/common/saa7146/ -F: drivers/media/pci/saa7146/ +F: drivers/staging/media/deprecated/saa7146/ F: include/media/drv-intf/saa7146* SAFESETID SECURITY MODULE diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index a2ae71270054..852b7d92fbdd 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -22,7 +22,6 @@ config VIDEO_TVEEPROM depends on I2C source "drivers/media/common/b2c2/Kconfig" -source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" source "drivers/media/common/v4l2-tpg/Kconfig" source "drivers/media/common/videobuf2/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index ad0b1e95fb12..d78a0df15478 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/ +obj-y += b2c2/ siano/ v4l2-tpg/ videobuf2/ # Please keep it alphabetically sorted by Kconfig name # (e. g. LC_ALL=C sort Makefile) diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig deleted file mode 100644 index a0aa155e5d85..000000000000 --- a/drivers/media/common/saa7146/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_SAA7146 - tristate - depends on I2C && PCI - -config VIDEO_SAA7146_VV - tristate - depends on VIDEO_DEV - select VIDEOBUF_DMA_SG - select VIDEO_SAA7146 diff --git a/drivers/media/common/saa7146/Makefile b/drivers/media/common/saa7146/Makefile deleted file mode 100644 index 2a6337feaec8..000000000000 --- a/drivers/media/common/saa7146/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -saa7146-objs := saa7146_i2c.o saa7146_core.o -saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o - -obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o -obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c deleted file mode 100644 index e50fa0ff7c5d..000000000000 --- a/drivers/media/common/saa7146/saa7146_core.c +++ /dev/null @@ -1,578 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - saa7146.o - driver for generic saa7146-based hardware - - Copyright (C) 1998-2003 Michael Hunold - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -static int saa7146_num; - -unsigned int saa7146_debug; - -module_param(saa7146_debug, uint, 0644); -MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); - -#if 0 -static void dump_registers(struct saa7146_dev* dev) -{ - int i = 0; - - pr_info(" @ %li jiffies:\n", jiffies); - for (i = 0; i <= 0x148; i += 4) - pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i)); -} -#endif - -/**************************************************************************** - * gpio and debi helper functions - ****************************************************************************/ - -void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) -{ - u32 value = 0; - - BUG_ON(port > 3); - - value = saa7146_read(dev, GPIO_CTRL); - value &= ~(0xff << (8*port)); - value |= (data << (8*port)); - saa7146_write(dev, GPIO_CTRL, value); -} - -/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ -static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev, - unsigned long us1, unsigned long us2) -{ - unsigned long timeout; - int err; - - /* wait for registers to be programmed */ - timeout = jiffies + usecs_to_jiffies(us1); - while (1) { - err = time_after(jiffies, timeout); - if (saa7146_read(dev, MC2) & 2) - break; - if (err) { - pr_debug("%s: %s timed out while waiting for registers getting programmed\n", - dev->name, __func__); - return -ETIMEDOUT; - } - msleep(1); - } - - /* wait for transfer to complete */ - timeout = jiffies + usecs_to_jiffies(us2); - while (1) { - err = time_after(jiffies, timeout); - if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) - break; - saa7146_read(dev, MC2); - if (err) { - DEB_S("%s: %s timed out while waiting for transfer completion\n", - dev->name, __func__); - return -ETIMEDOUT; - } - msleep(1); - } - - return 0; -} - -static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev, - unsigned long us1, unsigned long us2) -{ - unsigned long loops; - - /* wait for registers to be programmed */ - loops = us1; - while (1) { - if (saa7146_read(dev, MC2) & 2) - break; - if (!loops--) { - pr_err("%s: %s timed out while waiting for registers getting programmed\n", - dev->name, __func__); - return -ETIMEDOUT; - } - udelay(1); - } - - /* wait for transfer to complete */ - loops = us2 / 5; - while (1) { - if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) - break; - saa7146_read(dev, MC2); - if (!loops--) { - DEB_S("%s: %s timed out while waiting for transfer completion\n", - dev->name, __func__); - return -ETIMEDOUT; - } - udelay(5); - } - - return 0; -} - -int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) -{ - if (nobusyloop) - return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000); - else - return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000); -} - -/**************************************************************************** - * general helper functions - ****************************************************************************/ - -/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c - make sure virt has been allocated with vmalloc_32(), otherwise the BUG() - may be triggered on highmem machines */ -static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) -{ - struct scatterlist *sglist; - struct page *pg; - int i; - - sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); - if (NULL == sglist) - return NULL; - sg_init_table(sglist, nr_pages); - for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { - pg = vmalloc_to_page(virt); - if (NULL == pg) - goto err; - BUG_ON(PageHighMem(pg)); - sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); - } - return sglist; - - err: - kfree(sglist); - return NULL; -} - -/********************************************************************************/ -/* common page table functions */ - -void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) -{ - int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; - void *mem = vmalloc_32(length); - int slen = 0; - - if (NULL == mem) - goto err_null; - - if (!(pt->slist = vmalloc_to_sg(mem, pages))) - goto err_free_mem; - - if (saa7146_pgtable_alloc(pci, pt)) - goto err_free_slist; - - pt->nents = pages; - slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); - if (0 == slen) - goto err_free_pgtable; - - if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) - goto err_unmap_sg; - - return mem; - -err_unmap_sg: - dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); -err_free_pgtable: - saa7146_pgtable_free(pci, pt); -err_free_slist: - kfree(pt->slist); - pt->slist = NULL; -err_free_mem: - vfree(mem); -err_null: - return NULL; -} - -void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt) -{ - dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); - saa7146_pgtable_free(pci, pt); - kfree(pt->slist); - pt->slist = NULL; - vfree(mem); -} - -void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) -{ - if (NULL == pt->cpu) - return; - dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma); - pt->cpu = NULL; -} - -int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) -{ - __le32 *cpu; - dma_addr_t dma_addr = 0; - - cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL); - if (NULL == cpu) { - return -ENOMEM; - } - pt->size = PAGE_SIZE; - pt->cpu = cpu; - pt->dma = dma_addr; - - return 0; -} - -int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, - struct scatterlist *list, int sglen ) -{ - __le32 *ptr, fill; - int nr_pages = 0; - int i,p; - - BUG_ON(0 == sglen); - BUG_ON(list->offset > PAGE_SIZE); - - /* if we have a user buffer, the first page may not be - aligned to a page boundary. */ - pt->offset = list->offset; - - ptr = pt->cpu; - for (i = 0; i < sglen; i++, list++) { -/* - pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n", - i, sg_dma_address(list), sg_dma_len(list), - list->offset); -*/ - for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr++) { - *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096); - nr_pages++; - } - } - - - /* safety; fill the page table up with the last valid page */ - fill = *(ptr-1); - for(i=nr_pages;i<1024;i++) { - *ptr++ = fill; - } - -/* - ptr = pt->cpu; - pr_debug("offset: %d\n", pt->offset); - for(i=0;i<5;i++) { - pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]); - } -*/ - return 0; -} - -/********************************************************************************/ -/* interrupt handler */ -static irqreturn_t interrupt_hw(int irq, void *dev_id) -{ - struct saa7146_dev *dev = dev_id; - u32 isr; - u32 ack_isr; - - /* read out the interrupt status register */ - ack_isr = isr = saa7146_read(dev, ISR); - - /* is this our interrupt? */ - if ( 0 == isr ) { - /* nope, some other device */ - return IRQ_NONE; - } - - if (dev->ext) { - if (dev->ext->irq_mask & isr) { - if (dev->ext->irq_func) - dev->ext->irq_func(dev, &isr); - isr &= ~dev->ext->irq_mask; - } - } - if (0 != (isr & (MASK_27))) { - DEB_INT("irq: RPS0 (0x%08x)\n", isr); - if (dev->vv_data && dev->vv_callback) - dev->vv_callback(dev,isr); - isr &= ~MASK_27; - } - if (0 != (isr & (MASK_28))) { - if (dev->vv_data && dev->vv_callback) - dev->vv_callback(dev,isr); - isr &= ~MASK_28; - } - if (0 != (isr & (MASK_16|MASK_17))) { - SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); - /* only wake up if we expect something */ - if (0 != dev->i2c_op) { - dev->i2c_op = 0; - wake_up(&dev->i2c_wq); - } else { - u32 psr = saa7146_read(dev, PSR); - u32 ssr = saa7146_read(dev, SSR); - pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n", - dev->name, isr, psr, ssr); - } - isr &= ~(MASK_16|MASK_17); - } - if( 0 != isr ) { - ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n", - isr); - ERR("disabling interrupt source(s)!\n"); - SAA7146_IER_DISABLE(dev,isr); - } - saa7146_write(dev, ISR, ack_isr); - return IRQ_HANDLED; -} - -/*********************************************************************************/ -/* configuration-functions */ - -static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) -{ - struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; - struct saa7146_extension *ext = pci_ext->ext; - struct saa7146_dev *dev; - int err = -ENOMEM; - - /* clear out mem for sure */ - dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL); - if (!dev) { - ERR("out of memory\n"); - goto out; - } - - /* create a nice device name */ - sprintf(dev->name, "saa7146 (%d)", saa7146_num); - - DEB_EE("pci:%p\n", pci); - - err = pci_enable_device(pci); - if (err < 0) { - ERR("pci_enable_device() failed\n"); - goto err_free; - } - - /* enable bus-mastering */ - pci_set_master(pci); - - dev->pci = pci; - - /* get chip-revision; this is needed to enable bug-fixes */ - dev->revision = pci->revision; - - /* remap the memory from virtual to physical address */ - - err = pci_request_region(pci, 0, "saa7146"); - if (err < 0) - goto err_disable; - - dev->mem = ioremap(pci_resource_start(pci, 0), - pci_resource_len(pci, 0)); - if (!dev->mem) { - ERR("ioremap() failed\n"); - err = -ENODEV; - goto err_release; - } - - /* we don't do a master reset here anymore, it screws up - some boards that don't have an i2c-eeprom for configuration - values */ -/* - saa7146_write(dev, MC1, MASK_31); -*/ - - /* disable all irqs */ - saa7146_write(dev, IER, 0); - - /* shut down all dma transfers and rps tasks */ - saa7146_write(dev, MC1, 0x30ff0000); - - /* clear out any rps-signals pending */ - saa7146_write(dev, MC2, 0xf8000000); - - /* request an interrupt for the saa7146 */ - err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED, - dev->name, dev); - if (err < 0) { - ERR("request_irq() failed\n"); - goto err_unmap; - } - - err = -ENOMEM; - - /* get memory for various stuff */ - dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, - &dev->d_rps0.dma_handle, - GFP_KERNEL); - if (!dev->d_rps0.cpu_addr) - goto err_free_irq; - - dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, - &dev->d_rps1.dma_handle, - GFP_KERNEL); - if (!dev->d_rps1.cpu_addr) - goto err_free_rps0; - - dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, - &dev->d_i2c.dma_handle, GFP_KERNEL); - if (!dev->d_i2c.cpu_addr) - goto err_free_rps1; - - /* the rest + print status message */ - - pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n", - dev->mem, dev->revision, pci->irq, - pci->subsystem_vendor, pci->subsystem_device); - dev->ext = ext; - - mutex_init(&dev->v4l2_lock); - spin_lock_init(&dev->int_slock); - spin_lock_init(&dev->slock); - - mutex_init(&dev->i2c_lock); - - dev->module = THIS_MODULE; - init_waitqueue_head(&dev->i2c_wq); - - /* set some sane pci arbitrition values */ - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - - /* TODO: use the status code of the callback */ - - err = -ENODEV; - - if (ext->probe && ext->probe(dev)) { - DEB_D("ext->probe() failed for %p. skipping device.\n", dev); - goto err_free_i2c; - } - - if (ext->attach(dev, pci_ext)) { - DEB_D("ext->attach() failed for %p. skipping device.\n", dev); - goto err_free_i2c; - } - /* V4L extensions will set the pci drvdata to the v4l2_device in the - attach() above. So for those cards that do not use V4L we have to - set it explicitly. */ - pci_set_drvdata(pci, &dev->v4l2_dev); - - saa7146_num++; - - err = 0; -out: - return err; - -err_free_i2c: - dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, - dev->d_i2c.dma_handle); -err_free_rps1: - dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, - dev->d_rps1.dma_handle); -err_free_rps0: - dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, - dev->d_rps0.dma_handle); -err_free_irq: - free_irq(pci->irq, (void *)dev); -err_unmap: - iounmap(dev->mem); -err_release: - pci_release_region(pci, 0); -err_disable: - pci_disable_device(pci); -err_free: - kfree(dev); - goto out; -} - -static void saa7146_remove_one(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); - struct { - void *addr; - dma_addr_t dma; - } dev_map[] = { - { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, - { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, - { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, - { NULL, 0 } - }, *p; - - DEB_EE("dev:%p\n", dev); - - dev->ext->detach(dev); - - /* shut down all video dma transfers */ - saa7146_write(dev, MC1, 0x00ff0000); - - /* disable all irqs, release irq-routine */ - saa7146_write(dev, IER, 0); - - free_irq(pdev->irq, dev); - - for (p = dev_map; p->addr; p++) - dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr, - p->dma); - - iounmap(dev->mem); - pci_release_region(pdev, 0); - pci_disable_device(pdev); - kfree(dev); - - saa7146_num--; -} - -/*********************************************************************************/ -/* extension handling functions */ - -int saa7146_register_extension(struct saa7146_extension* ext) -{ - DEB_EE("ext:%p\n", ext); - - ext->driver.name = ext->name; - ext->driver.id_table = ext->pci_tbl; - ext->driver.probe = saa7146_init_one; - ext->driver.remove = saa7146_remove_one; - - pr_info("register extension '%s'\n", ext->name); - return pci_register_driver(&ext->driver); -} - -int saa7146_unregister_extension(struct saa7146_extension* ext) -{ - DEB_EE("ext:%p\n", ext); - pr_info("unregister extension '%s'\n", ext->name); - pci_unregister_driver(&ext->driver); - return 0; -} - -EXPORT_SYMBOL_GPL(saa7146_register_extension); -EXPORT_SYMBOL_GPL(saa7146_unregister_extension); - -/* misc functions used by extension modules */ -EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); -EXPORT_SYMBOL_GPL(saa7146_pgtable_free); -EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); -EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); -EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable); -EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); - -EXPORT_SYMBOL_GPL(saa7146_setgpio); - -EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); - -EXPORT_SYMBOL_GPL(saa7146_debug); - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c deleted file mode 100644 index e9a15de6126e..000000000000 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ /dev/null @@ -1,658 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -/****************************************************************************/ -/* resource management functions, shamelessly stolen from saa7134 driver */ - -int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - - if (fh->resources & bit) { - DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n", - bit, vv->resources); - /* have it already allocated */ - return 1; - } - - /* is it free? */ - if (vv->resources & bit) { - DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n", - vv->resources, bit); - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - vv->resources |= bit; - DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources); - return 1; -} - -void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - vv->resources &= ~bits; - DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources); -} - - -/********************************************************************************/ -/* common dma functions */ - -void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, - struct saa7146_buf *buf) -{ - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - DEB_EE("dev:%p, buf:%p\n", dev, buf); - - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - - -/********************************************************************************/ -/* common buffer functions */ - -int saa7146_buffer_queue(struct saa7146_dev *dev, - struct saa7146_dmaqueue *q, - struct saa7146_buf *buf) -{ - assert_spin_locked(&dev->slock); - DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf); - - BUG_ON(!q); - - if (NULL == q->curr) { - q->curr = buf; - DEB_D("immediately activating buffer %p\n", buf); - buf->activate(dev,buf,NULL); - } else { - list_add_tail(&buf->vb.queue,&q->queue); - buf->vb.state = VIDEOBUF_QUEUED; - DEB_D("adding buffer %p to queue. (active buffer present)\n", - buf); - } - return 0; -} - -void saa7146_buffer_finish(struct saa7146_dev *dev, - struct saa7146_dmaqueue *q, - int state) -{ - assert_spin_locked(&dev->slock); - DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); - DEB_EE("q->curr:%p\n", q->curr); - - /* finish current buffer */ - if (NULL == q->curr) { - DEB_D("aiii. no current buffer\n"); - return; - } - - q->curr->vb.state = state; - q->curr->vb.ts = ktime_get_ns(); - wake_up(&q->curr->vb.done); - - q->curr = NULL; -} - -void saa7146_buffer_next(struct saa7146_dev *dev, - struct saa7146_dmaqueue *q, int vbi) -{ - struct saa7146_buf *buf,*next = NULL; - - BUG_ON(!q); - - DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi); - - assert_spin_locked(&dev->slock); - if (!list_empty(&q->queue)) { - /* activate next one from queue */ - buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue); - list_del(&buf->vb.queue); - if (!list_empty(&q->queue)) - next = list_entry(q->queue.next,struct saa7146_buf, vb.queue); - q->curr = buf; - DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n", - buf, q->queue.prev, q->queue.next); - buf->activate(dev,buf,next); - } else { - DEB_INT("no next buffer. stopping.\n"); - if( 0 != vbi ) { - /* turn off video-dma3 */ - saa7146_write(dev,MC1, MASK_20); - } else { - /* nothing to do -- just prevent next video-dma1 transfer - by lowering the protection address */ - - // fixme: fix this for vflip != 0 - - saa7146_write(dev, PROT_ADDR1, 0); - saa7146_write(dev, MC2, (MASK_02|MASK_18)); - - /* write the address of the rps-program */ - saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); - /* turn on rps */ - saa7146_write(dev, MC1, (MASK_12 | MASK_28)); - -/* - printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); - printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); - printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); - printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); - printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); - printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); -*/ - } - del_timer(&q->timeout); - } -} - -void saa7146_buffer_timeout(struct timer_list *t) -{ - struct saa7146_dmaqueue *q = from_timer(q, t, timeout); - struct saa7146_dev *dev = q->dev; - unsigned long flags; - - DEB_EE("dev:%p, dmaq:%p\n", dev, q); - - spin_lock_irqsave(&dev->slock,flags); - if (q->curr) { - DEB_D("timeout on %p\n", q->curr); - saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR); - } - - /* we don't restart the transfer here like other drivers do. when - a streaming capture is disabled, the timeout function will be - called for the current buffer. if we activate the next buffer now, - we mess up our capture logic. if a timeout occurs on another buffer, - then something is seriously broken before, so no need to buffer the - next capture IMHO... */ -/* - saa7146_buffer_next(dev,q); -*/ - spin_unlock_irqrestore(&dev->slock,flags); -} - -/********************************************************************************/ -/* file operations */ - -static int fops_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_dev *dev = video_drvdata(file); - struct saa7146_fh *fh = NULL; - int result = 0; - - DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); - - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - - DEB_D("using: %p\n", dev); - - /* check if an extension is registered */ - if( NULL == dev->ext ) { - DEB_S("no extension registered for this device\n"); - result = -ENODEV; - goto out; - } - - /* allocate per open data */ - fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) { - DEB_S("cannot allocate memory for per open data\n"); - result = -ENOMEM; - goto out; - } - - v4l2_fh_init(&fh->fh, vdev); - - file->private_data = &fh->fh; - fh->dev = dev; - - if (vdev->vfl_type == VFL_TYPE_VBI) { - DEB_S("initializing vbi...\n"); - if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) - result = saa7146_vbi_uops.open(dev,file); - if (dev->ext_vv_data->vbi_fops.open) - dev->ext_vv_data->vbi_fops.open(file); - } else { - DEB_S("initializing video...\n"); - result = saa7146_video_uops.open(dev,file); - } - - if (0 != result) { - goto out; - } - - if( 0 == try_module_get(dev->ext->module)) { - result = -EINVAL; - goto out; - } - - result = 0; - v4l2_fh_add(&fh->fh); -out: - if (fh && result != 0) { - kfree(fh); - file->private_data = NULL; - } - mutex_unlock(vdev->lock); - return result; -} - -static int fops_release(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - - DEB_EE("file:%p\n", file); - - mutex_lock(vdev->lock); - - if (vdev->vfl_type == VFL_TYPE_VBI) { - if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) - saa7146_vbi_uops.release(dev,file); - if (dev->ext_vv_data->vbi_fops.release) - dev->ext_vv_data->vbi_fops.release(file); - } else { - saa7146_video_uops.release(dev,file); - } - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - module_put(dev->ext->module); - file->private_data = NULL; - kfree(fh); - - mutex_unlock(vdev->lock); - - return 0; -} - -static int fops_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_fh *fh = file->private_data; - struct videobuf_queue *q; - int res; - - switch (vdev->vfl_type) { - case VFL_TYPE_VIDEO: { - DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", - file, vma); - q = &fh->video_q; - break; - } - case VFL_TYPE_VBI: { - DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", - file, vma); - if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) - return -ENODEV; - q = &fh->vbi_q; - break; - } - default: - BUG(); - } - - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - res = videobuf_mmap_mapper(q, vma); - mutex_unlock(vdev->lock); - return res; -} - -static __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_fh *fh = file->private_data; - struct videobuf_buffer *buf = NULL; - struct videobuf_queue *q; - __poll_t res = v4l2_ctrl_poll(file, wait); - - DEB_EE("file:%p, poll:%p\n", file, wait); - - if (vdev->vfl_type == VFL_TYPE_VBI) { - if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) - return res | EPOLLOUT | EPOLLWRNORM; - if( 0 == fh->vbi_q.streaming ) - return res | videobuf_poll_stream(file, &fh->vbi_q, wait); - q = &fh->vbi_q; - } else { - DEB_D("using video queue\n"); - q = &fh->video_q; - } - - if (!list_empty(&q->stream)) - buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - - if (!buf) { - DEB_D("buf == NULL!\n"); - return res | EPOLLERR; - } - - poll_wait(file, &buf->done, wait); - if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { - DEB_D("poll succeeded!\n"); - return res | EPOLLIN | EPOLLRDNORM; - } - - DEB_D("nothing to poll for, buf->state:%d\n", buf->state); - return res; -} - -static __poll_t fops_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - __poll_t res; - - mutex_lock(vdev->lock); - res = __fops_poll(file, wait); - mutex_unlock(vdev->lock); - return res; -} - -static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_fh *fh = file->private_data; - int ret; - - switch (vdev->vfl_type) { - case VFL_TYPE_VIDEO: -/* - DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", - file, data, (unsigned long)count); -*/ - return saa7146_video_uops.read(file,data,count,ppos); - case VFL_TYPE_VBI: -/* - DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", - file, data, (unsigned long)count); -*/ - if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) { - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = saa7146_vbi_uops.read(file, data, count, ppos); - mutex_unlock(vdev->lock); - return ret; - } - return -EINVAL; - default: - BUG(); - } -} - -static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146_fh *fh = file->private_data; - int ret; - - switch (vdev->vfl_type) { - case VFL_TYPE_VIDEO: - return -EINVAL; - case VFL_TYPE_VBI: - if (fh->dev->ext_vv_data->vbi_fops.write) { - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); - mutex_unlock(vdev->lock); - return ret; - } - return -EINVAL; - default: - BUG(); - } -} - -static const struct v4l2_file_operations video_fops = -{ - .owner = THIS_MODULE, - .open = fops_open, - .release = fops_release, - .read = fops_read, - .write = fops_write, - .poll = fops_poll, - .mmap = fops_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static void vv_callback(struct saa7146_dev *dev, unsigned long status) -{ - u32 isr = status; - - DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status); - - if (0 != (isr & (MASK_27))) { - DEB_INT("irq: RPS0 (0x%08x)\n", isr); - saa7146_video_uops.irq_done(dev,isr); - } - - if (0 != (isr & (MASK_28))) { - u32 mc2 = saa7146_read(dev, MC2); - if( 0 != (mc2 & MASK_15)) { - DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr); - wake_up(&dev->vv_data->vbi_wq); - saa7146_write(dev,MC2, MASK_31); - return; - } - DEB_INT("irq: RPS1 (0x%08x)\n", isr); - saa7146_vbi_uops.irq_done(dev,isr); - } -} - -static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { - .s_ctrl = saa7146_s_ctrl, -}; - -int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) -{ - struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; - struct v4l2_pix_format *fmt; - struct v4l2_vbi_format *vbi; - struct saa7146_vv *vv; - int err; - - err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); - if (err) - return err; - - v4l2_ctrl_handler_init(hdl, 6); - v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); - v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - if (hdl->error) { - err = hdl->error; - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); - return err; - } - dev->v4l2_dev.ctrl_handler = hdl; - - vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); - if (vv == NULL) { - ERR("out of memory. aborting.\n"); - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); - return -ENOMEM; - } - ext_vv->vid_ops = saa7146_video_ioctl_ops; - ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; - ext_vv->core_ops = &saa7146_video_ioctl_ops; - - DEB_EE("dev:%p\n", dev); - - /* set default values for video parts of the saa7146 */ - saa7146_write(dev, BCS_CTRL, 0x80400040); - - /* enable video-port pins */ - saa7146_write(dev, MC1, (MASK_10 | MASK_26)); - - /* save per-device extension data (one extension can - handle different devices that might need different - configuration data) */ - dev->ext_vv_data = ext_vv; - - vv->d_clipping.cpu_addr = - dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, - &vv->d_clipping.dma_handle, GFP_KERNEL); - if( NULL == vv->d_clipping.cpu_addr ) { - ERR("out of memory. aborting.\n"); - kfree(vv); - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); - return -ENOMEM; - } - - saa7146_video_uops.init(dev,vv); - if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) - saa7146_vbi_uops.init(dev,vv); - - vv->ov_fb.fmt.width = vv->standard->h_max_out; - vv->ov_fb.fmt.height = vv->standard->v_max_out; - vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; - vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width; - vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height; - vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; - - fmt = &vv->video_fmt; - fmt->width = 384; - fmt->height = 288; - fmt->pixelformat = V4L2_PIX_FMT_BGR24; - fmt->field = V4L2_FIELD_ANY; - fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->bytesperline = 3 * fmt->width; - fmt->sizeimage = fmt->bytesperline * fmt->height; - - vbi = &vv->vbi_fmt; - vbi->sampling_rate = 27000000; - vbi->offset = 248; /* todo */ - vbi->samples_per_line = 720 * 2; - vbi->sample_format = V4L2_PIX_FMT_GREY; - - /* fixme: this only works for PAL */ - vbi->start[0] = 5; - vbi->count[0] = 16; - vbi->start[1] = 312; - vbi->count[1] = 16; - - timer_setup(&vv->vbi_read_timeout, NULL, 0); - - vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; - vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; - dev->vv_data = vv; - dev->vv_callback = &vv_callback; - - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_vv_init); - -int saa7146_vv_release(struct saa7146_dev* dev) -{ - struct saa7146_vv *vv = dev->vv_data; - - DEB_EE("dev:%p\n", dev); - - v4l2_device_unregister(&dev->v4l2_dev); - dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, - vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); - v4l2_ctrl_handler_free(&dev->ctrl_handler); - kfree(vv); - dev->vv_data = NULL; - dev->vv_callback = NULL; - - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_vv_release); - -int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, - char *name, int type) -{ - int err; - int i; - - DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); - - vfd->fops = &video_fops; - if (type == VFL_TYPE_VIDEO) - vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; - else - vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; - vfd->release = video_device_release_empty; - vfd->lock = &dev->v4l2_lock; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->tvnorms = 0; - for (i = 0; i < dev->ext_vv_data->num_stds; i++) - vfd->tvnorms |= dev->ext_vv_data->stds[i].id; - strscpy(vfd->name, name, sizeof(vfd->name)); - vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - vfd->device_caps |= dev->ext_vv_data->capabilities; - if (type == VFL_TYPE_VIDEO) - vfd->device_caps &= - ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); - else - vfd->device_caps &= - ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); - video_set_drvdata(vfd, dev); - - err = video_register_device(vfd, type, -1); - if (err < 0) { - ERR("cannot register v4l2 device. skipping.\n"); - return err; - } - - pr_info("%s: registered device %s [v4l2]\n", - dev->name, video_device_node_name(vfd)); - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_register_device); - -int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev) -{ - DEB_EE("dev:%p\n", dev); - - video_unregister_device(vfd); - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_unregister_device); - -static int __init saa7146_vv_init_module(void) -{ - return 0; -} - - -static void __exit saa7146_vv_cleanup_module(void) -{ -} - -module_init(saa7146_vv_init_module); -module_exit(saa7146_vv_cleanup_module); - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c deleted file mode 100644 index 6c9946a402ee..000000000000 --- a/drivers/media/common/saa7146/saa7146_hlp.c +++ /dev/null @@ -1,1046 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format) -{ - /* clear out the necessary bits */ - *clip_format &= 0x0000ffff; - /* set these bits new */ - *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16)); -} - -static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl) -{ - *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28); - *hps_ctrl |= (source << 30) | (sync << 28); -} - -static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl) -{ - int hyo = 0, hxo = 0; - - hyo = vv->standard->v_offset; - hxo = vv->standard->h_offset; - - *hps_h_scale &= ~(MASK_B0 | 0xf00); - *hps_h_scale |= (hxo << 0); - - *hps_ctrl &= ~(MASK_W0 | MASK_B2); - *hps_ctrl |= (hyo << 12); -} - -/* helper functions for the calculation of the horizontal- and vertical - scaling registers, clip-format-register etc ... - these functions take pointers to the (most-likely read-out - original-values) and manipulate them according to the requested - changes. -*/ - -/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */ -static struct { - u16 hps_coeff; - u16 weight_sum; -} hps_h_coeff_tab [] = { - {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8}, - {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8}, - {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8}, - {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8}, - {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8}, - {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8}, - {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, - {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, - {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, - {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8}, - {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, - {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8}, - {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16} -}; - -/* table of attenuation values for horizontal scaling */ -static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; - -/* calculate horizontal scale registers */ -static int calculate_h_scale_registers(struct saa7146_dev *dev, - int in_x, int out_x, int flip_lr, - u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale) -{ - /* horizontal prescaler */ - u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0; - /* horizontal scaler */ - u32 xim = 0, xp = 0, xsci =0; - /* vertical scale & gain */ - u32 pfuv = 0; - - /* helper variables */ - u32 h_atten = 0, i = 0; - - if ( 0 == out_x ) { - return -EINVAL; - } - - /* mask out vanity-bit */ - *hps_ctrl &= ~MASK_29; - - /* calculate prescale-(xspc)-value: [n .. 1/2) : 1 - [1/2 .. 1/3) : 2 - [1/3 .. 1/4) : 3 - ... */ - if (in_x > out_x) { - xpsc = in_x / out_x; - } - else { - /* zooming */ - xpsc = 1; - } - - /* if flip_lr-bit is set, number of pixels after - horizontal prescaling must be < 384 */ - if ( 0 != flip_lr ) { - - /* set vanity bit */ - *hps_ctrl |= MASK_29; - - while (in_x / xpsc >= 384 ) - xpsc++; - } - /* if zooming is wanted, number of pixels after - horizontal prescaling must be < 768 */ - else { - while ( in_x / xpsc >= 768 ) - xpsc++; - } - - /* maximum prescale is 64 (p.69) */ - if ( xpsc > 64 ) - xpsc = 64; - - /* keep xacm clear*/ - xacm = 0; - - /* set horizontal filter parameters (CXY = CXUV) */ - cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff; - cxuv = cxy; - - /* calculate and set horizontal fine scale (xsci) */ - - /* bypass the horizontal scaler ? */ - if ( (in_x == out_x) && ( 1 == xpsc ) ) - xsci = 0x400; - else - xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc; - - /* set start phase for horizontal fine scale (xp) to 0 */ - xp = 0; - - /* set xim, if we bypass the horizontal scaler */ - if ( 0x400 == xsci ) - xim = 1; - else - xim = 0; - - /* if the prescaler is bypassed, enable horizontal - accumulation mode (xacm) and clear dcgx */ - if( 1 == xpsc ) { - xacm = 1; - dcgx = 0; - } else { - xacm = 0; - /* get best match in the table of attenuations - for horizontal scaling */ - h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum; - - for (i = 0; h_attenuation[i] != 0; i++) { - if (h_attenuation[i] >= h_atten) - break; - } - - dcgx = i; - } - - /* the horizontal scaling increment controls the UV filter - to reduce the bandwidth to improve the display quality, - so set it ... */ - if ( xsci == 0x400) - pfuv = 0x00; - else if ( xsci < 0x600) - pfuv = 0x01; - else if ( xsci < 0x680) - pfuv = 0x11; - else if ( xsci < 0x700) - pfuv = 0x22; - else - pfuv = 0x33; - - - *hps_v_gain &= MASK_W0|MASK_B2; - *hps_v_gain |= (pfuv << 24); - - *hps_h_scale &= ~(MASK_W1 | 0xf000); - *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12); - - *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0); - - return 0; -} - -static struct { - u16 hps_coeff; - u16 weight_sum; -} hps_v_coeff_tab [] = { - {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8}, - {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16}, - {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16}, - {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32}, - {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32}, - {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32}, - {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, - {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, - {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, - {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64}, - {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64}, - {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64}, - {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128} -}; - -/* table of attenuation values for vertical scaling */ -static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; - -/* calculate vertical scale registers */ -static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field, - int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain) -{ - int lpi = 0; - - /* vertical scaling */ - u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0; - /* vertical scale & gain */ - u32 dcgy = 0, cya_cyb = 0; - - /* helper variables */ - u32 v_atten = 0, i = 0; - - /* error, if vertical zooming */ - if ( in_y < out_y ) { - return -EINVAL; - } - - /* linear phase interpolation may be used - if scaling is between 1 and 1/2 (both fields used) - or scaling is between 1/2 and 1/4 (if only one field is used) */ - - if (V4L2_FIELD_HAS_BOTH(field)) { - if( 2*out_y >= in_y) { - lpi = 1; - } - } else if (field == V4L2_FIELD_TOP - || field == V4L2_FIELD_ALTERNATE - || field == V4L2_FIELD_BOTTOM) { - if( 4*out_y >= in_y ) { - lpi = 1; - } - out_y *= 2; - } - if( 0 != lpi ) { - - yacm = 0; - yacl = 0; - cya_cyb = 0x00ff; - - /* calculate scaling increment */ - if ( in_y > out_y ) - ysci = ((1024 * in_y) / (out_y + 1)) - 1024; - else - ysci = 0; - - dcgy = 0; - - /* calculate ype and ypo */ - ype = ysci / 16; - ypo = ype + (ysci / 64); - - } else { - yacm = 1; - - /* calculate scaling increment */ - ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10; - - /* calculate ype and ypo */ - ypo = ype = ((ysci + 15) / 16); - - /* the sequence length interval (yacl) has to be set according - to the prescale value, e.g. [n .. 1/2) : 0 - [1/2 .. 1/3) : 1 - [1/3 .. 1/4) : 2 - ... */ - if ( ysci < 512) { - yacl = 0; - } else { - yacl = ( ysci / (1024 - ysci) ); - } - - /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ - cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff; - - /* get best match in the table of attenuations for vertical scaling */ - v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum; - - for (i = 0; v_attenuation[i] != 0; i++) { - if (v_attenuation[i] >= v_atten) - break; - } - - dcgy = i; - } - - /* ypo and ype swapped in spec ? */ - *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1); - - *hps_v_gain &= ~(MASK_W0|MASK_B2); - *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0); - - return 0; -} - -/* simple bubble-sort algorithm with duplicate elimination */ -static int sort_and_eliminate(u32* values, int* count) -{ - int low = 0, high = 0, top = 0; - int cur = 0, next = 0; - - /* sanity checks */ - if( (0 > *count) || (NULL == values) ) { - return -EINVAL; - } - - /* bubble sort the first @count items of the array @values */ - for( top = *count; top > 0; top--) { - for( low = 0, high = 1; high < top; low++, high++) { - if( values[low] > values[high] ) - swap(values[low], values[high]); - } - } - - /* remove duplicate items */ - for( cur = 0, next = 1; next < *count; next++) { - if( values[cur] != values[next]) - values[++cur] = values[next]; - } - - *count = cur + 1; - - return 0; -} - -static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh, - struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) -{ - struct saa7146_vv *vv = dev->vv_data; - __le32 *clipping = vv->d_clipping.cpu_addr; - - int width = vv->ov.win.w.width; - int height = vv->ov.win.w.height; - int clipcount = vv->ov.nclips; - - u32 line_list[32]; - u32 pixel_list[32]; - int numdwords = 0; - - int i = 0, j = 0; - int cnt_line = 0, cnt_pixel = 0; - - int x[32], y[32], w[32], h[32]; - - /* clear out memory */ - memset(&line_list[0], 0x00, sizeof(u32)*32); - memset(&pixel_list[0], 0x00, sizeof(u32)*32); - memset(clipping, 0x00, SAA7146_CLIPPING_MEM); - - /* fill the line and pixel-lists */ - for(i = 0; i < clipcount; i++) { - int l = 0, r = 0, t = 0, b = 0; - - x[i] = vv->ov.clips[i].c.left; - y[i] = vv->ov.clips[i].c.top; - w[i] = vv->ov.clips[i].c.width; - h[i] = vv->ov.clips[i].c.height; - - if( w[i] < 0) { - x[i] += w[i]; w[i] = -w[i]; - } - if( h[i] < 0) { - y[i] += h[i]; h[i] = -h[i]; - } - if( x[i] < 0) { - w[i] += x[i]; x[i] = 0; - } - if( y[i] < 0) { - h[i] += y[i]; y[i] = 0; - } - if( 0 != vv->vflip ) { - y[i] = height - y[i] - h[i]; - } - - l = x[i]; - r = x[i]+w[i]; - t = y[i]; - b = y[i]+h[i]; - - /* insert left/right coordinates */ - pixel_list[ 2*i ] = min_t(int, l, width); - pixel_list[(2*i)+1] = min_t(int, r, width); - /* insert top/bottom coordinates */ - line_list[ 2*i ] = min_t(int, t, height); - line_list[(2*i)+1] = min_t(int, b, height); - } - - /* sort and eliminate lists */ - cnt_line = cnt_pixel = 2*clipcount; - sort_and_eliminate( &pixel_list[0], &cnt_pixel ); - sort_and_eliminate( &line_list[0], &cnt_line ); - - /* calculate the number of used u32s */ - numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2; - numdwords = max_t(int, 4, numdwords); - numdwords = min_t(int, 64, numdwords); - - /* fill up cliptable */ - for(i = 0; i < cnt_pixel; i++) { - clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16); - } - for(i = 0; i < cnt_line; i++) { - clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16); - } - - /* fill up cliptable with the display infos */ - for(j = 0; j < clipcount; j++) { - - for(i = 0; i < cnt_pixel; i++) { - - if( x[j] < 0) - x[j] = 0; - - if( pixel_list[i] < (x[j] + w[j])) { - - if ( pixel_list[i] >= x[j] ) { - clipping[2*i] |= cpu_to_le32(1 << j); - } - } - } - for(i = 0; i < cnt_line; i++) { - - if( y[j] < 0) - y[j] = 0; - - if( line_list[i] < (y[j] + h[j]) ) { - - if( line_list[i] >= y[j] ) { - clipping[(2*i)+1] |= cpu_to_le32(1 << j); - } - } - } - } - - /* adjust arbitration control register */ - *arbtr_ctrl &= 0xffff00ff; - *arbtr_ctrl |= 0x00001c00; - - vdma2->base_even = vv->d_clipping.dma_handle; - vdma2->base_odd = vv->d_clipping.dma_handle; - vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords)); - vdma2->base_page = 0x04; - vdma2->pitch = 0x00; - vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) ); - - /* set clipping-mode. this depends on the field(s) used */ - *clip_format &= 0xfffffff7; - if (V4L2_FIELD_HAS_BOTH(field)) { - *clip_format |= 0x00000008; - } else { - *clip_format |= 0x00000000; - } -} - -/* disable clipping */ -static void saa7146_disable_clipping(struct saa7146_dev *dev) -{ - u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); - - /* mask out relevant bits (=lower word)*/ - clip_format &= MASK_W1; - - /* upload clipping-registers*/ - saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); - saa7146_write(dev, MC2, (MASK_05 | MASK_21)); - - /* disable video dma2 */ - saa7146_write(dev, MC1, MASK_21); -} - -static void saa7146_set_clipping_rect(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - enum v4l2_field field = vv->ov.win.field; - struct saa7146_video_dma vdma2; - u32 clip_format; - u32 arbtr_ctrl; - - /* check clipcount, disable clipping if clipcount == 0*/ - if (vv->ov.nclips == 0) { - saa7146_disable_clipping(dev); - return; - } - - clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); - arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); - - calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field); - - /* set clipping format */ - clip_format &= 0xffff0008; - clip_format |= (SAA7146_CLIPPING_RECT << 4); - - /* prepare video dma2 */ - saa7146_write(dev, BASE_EVEN2, vdma2.base_even); - saa7146_write(dev, BASE_ODD2, vdma2.base_odd); - saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr); - saa7146_write(dev, BASE_PAGE2, vdma2.base_page); - saa7146_write(dev, PITCH2, vdma2.pitch); - saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte); - - /* prepare the rest */ - saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); - saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); - - /* upload clip_control-register, clipping-registers, enable video dma2 */ - saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19)); - saa7146_write(dev, MC1, (MASK_05 | MASK_21)); -} - -static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) -{ - struct saa7146_vv *vv = dev->vv_data; - - int source = vv->current_hps_source; - int sync = vv->current_hps_sync; - - u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0; - - /* set vertical scale */ - hps_v_scale = 0; /* all bits get set by the function-call */ - hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/ - calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain); - - /* set horizontal scale */ - hps_ctrl = 0; - hps_h_prescale = 0; /* all bits get set in the function */ - hps_h_scale = 0; - calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale); - - /* set hyo and hxo */ - calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl); - calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl); - - /* write out new register contents */ - saa7146_write(dev, HPS_V_SCALE, hps_v_scale); - saa7146_write(dev, HPS_V_GAIN, hps_v_gain); - saa7146_write(dev, HPS_CTRL, hps_ctrl); - saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale); - saa7146_write(dev, HPS_H_SCALE, hps_h_scale); - - /* upload shadow-ram registers */ - saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); -} - -/* calculate the new memory offsets for a desired position */ -static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat) -{ - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat); - - int b_depth = vv->ov_fmt->depth; - int b_bpl = vv->ov_fb.fmt.bytesperline; - /* The unsigned long cast is to remove a 64-bit compile warning since - it looks like a 64-bit address is cast to a 32-bit value, even - though the base pointer is really a 32-bit physical address that - goes into a 32-bit DMA register. - FIXME: might not work on some 64-bit platforms, but see the FIXME - in struct v4l2_framebuffer (videodev2.h) for that. - */ - u32 base = (u32)(unsigned long)vv->ov_fb.base; - - struct saa7146_video_dma vdma1; - - /* calculate memory offsets for picture, look if we shall top-down-flip */ - vdma1.pitch = 2*b_bpl; - if ( 0 == vv->vflip ) { - vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); - vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2); - vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2)); - } - else { - vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); - vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2); - vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2)); - } - - if (V4L2_FIELD_HAS_BOTH(field)) { - } else if (field == V4L2_FIELD_ALTERNATE) { - /* fixme */ - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - } else if (field == V4L2_FIELD_TOP) { - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - } else if (field == V4L2_FIELD_BOTTOM) { - vdma1.base_odd = vdma1.base_even; - vdma1.base_even = vdma1.prot_addr; - vdma1.pitch /= 2; - } - - if ( 0 != vv->vflip ) { - vdma1.pitch *= -1; - } - - vdma1.base_page = sfmt->swap; - vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels; - - saa7146_write_out_dma(dev, 1, &vdma1); -} - -static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) -{ - u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); - - /* call helper function */ - calculate_output_format_register(dev,palette,&clip_format); - - /* update the hps registers */ - saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format); - saa7146_write(dev, MC2, (MASK_05 | MASK_21)); -} - -/* select input-source */ -void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync) -{ - struct saa7146_vv *vv = dev->vv_data; - u32 hps_ctrl = 0; - - /* read old state */ - hps_ctrl = saa7146_read(dev, HPS_CTRL); - - hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 ); - hps_ctrl |= (source << 30) | (sync << 28); - - /* write back & upload register */ - saa7146_write(dev, HPS_CTRL, hps_ctrl); - saa7146_write(dev, MC2, (MASK_05 | MASK_21)); - - vv->current_hps_source = source; - vv->current_hps_sync = sync; -} -EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); - -int saa7146_enable_overlay(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - - saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); - saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); - saa7146_set_output_format(dev, vv->ov_fmt->trans); - saa7146_set_clipping_rect(fh); - - /* enable video dma1 */ - saa7146_write(dev, MC1, (MASK_06 | MASK_22)); - return 0; -} - -void saa7146_disable_overlay(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - - /* disable clipping + video dma1 */ - saa7146_disable_clipping(dev); - saa7146_write(dev, MC1, MASK_22); -} - -void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) -{ - int where = 0; - - if( which < 1 || which > 3) { - return; - } - - /* calculate starting address */ - where = (which-1)*0x18; - - saa7146_write(dev, where, vdma->base_odd); - saa7146_write(dev, where+0x04, vdma->base_even); - saa7146_write(dev, where+0x08, vdma->prot_addr); - saa7146_write(dev, where+0x0c, vdma->pitch); - saa7146_write(dev, where+0x10, vdma->base_page); - saa7146_write(dev, where+0x14, vdma->num_line_byte); - - /* upload */ - saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1))); -/* - printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even); - printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd); - printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr); - printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page); - printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch); - printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte); -*/ -} - -static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) -{ - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_video_dma vdma1; - - struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); - - int width = buf->fmt->width; - int height = buf->fmt->height; - int bytesperline = buf->fmt->bytesperline; - enum v4l2_field field = buf->fmt->field; - - int depth = sfmt->depth; - - DEB_CAP("[size=%dx%d,fields=%s]\n", - width, height, v4l2_field_names[field]); - - if( bytesperline != 0) { - vdma1.pitch = bytesperline*2; - } else { - vdma1.pitch = (width*depth*2)/8; - } - vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); - vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap; - - if( 0 != vv->vflip ) { - vdma1.prot_addr = buf->pt[0].offset; - vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height; - vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); - } else { - vdma1.base_even = buf->pt[0].offset; - vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); - vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height; - } - - if (V4L2_FIELD_HAS_BOTH(field)) { - } else if (field == V4L2_FIELD_ALTERNATE) { - /* fixme */ - if ( vv->last_field == V4L2_FIELD_TOP ) { - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { - vdma1.base_odd = vdma1.base_even; - vdma1.base_even = vdma1.prot_addr; - vdma1.pitch /= 2; - } - } else if (field == V4L2_FIELD_TOP) { - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - } else if (field == V4L2_FIELD_BOTTOM) { - vdma1.base_odd = vdma1.base_even; - vdma1.base_even = vdma1.prot_addr; - vdma1.pitch /= 2; - } - - if( 0 != vv->vflip ) { - vdma1.pitch *= -1; - } - - saa7146_write_out_dma(dev, 1, &vdma1); - return 0; -} - -static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) -{ - int height = buf->fmt->height; - int width = buf->fmt->width; - - vdma2->pitch = width; - vdma3->pitch = width; - - /* fixme: look at bytesperline! */ - - if( 0 != vv->vflip ) { - vdma2->prot_addr = buf->pt[1].offset; - vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset; - vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); - - vdma3->prot_addr = buf->pt[2].offset; - vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset; - vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); - } else { - vdma3->base_even = buf->pt[2].offset; - vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2); - vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; - - vdma2->base_even = buf->pt[1].offset; - vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2); - vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; - } - - return 0; -} - -static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) -{ - int height = buf->fmt->height; - int width = buf->fmt->width; - - vdma2->pitch = width/2; - vdma3->pitch = width/2; - - if( 0 != vv->vflip ) { - vdma2->prot_addr = buf->pt[2].offset; - vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset; - vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); - - vdma3->prot_addr = buf->pt[1].offset; - vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset; - vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); - - } else { - vdma3->base_even = buf->pt[2].offset; - vdma3->base_odd = vdma3->base_even + (vdma3->pitch); - vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; - - vdma2->base_even = buf->pt[1].offset; - vdma2->base_odd = vdma2->base_even + (vdma2->pitch); - vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; - } - return 0; -} - -static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) -{ - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_video_dma vdma1; - struct saa7146_video_dma vdma2; - struct saa7146_video_dma vdma3; - - struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); - - int width = buf->fmt->width; - int height = buf->fmt->height; - enum v4l2_field field = buf->fmt->field; - - BUG_ON(0 == buf->pt[0].dma); - BUG_ON(0 == buf->pt[1].dma); - BUG_ON(0 == buf->pt[2].dma); - - DEB_CAP("[size=%dx%d,fields=%s]\n", - width, height, v4l2_field_names[field]); - - /* fixme: look at bytesperline! */ - - /* fixme: what happens for user space buffers here?. The offsets are - most likely wrong, this version here only works for page-aligned - buffers, modifications to the pagetable-functions are necessary...*/ - - vdma1.pitch = width*2; - vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); - vdma1.base_page = buf->pt[0].dma | ME1; - - if( 0 != vv->vflip ) { - vdma1.prot_addr = buf->pt[0].offset; - vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset; - vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); - } else { - vdma1.base_even = buf->pt[0].offset; - vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); - vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset; - } - - vdma2.num_line_byte = 0; /* unused */ - vdma2.base_page = buf->pt[1].dma | ME1; - - vdma3.num_line_byte = 0; /* unused */ - vdma3.base_page = buf->pt[2].dma | ME1; - - switch( sfmt->depth ) { - case 12: { - calc_planar_420(vv,buf,&vdma2,&vdma3); - break; - } - case 16: { - calc_planar_422(vv,buf,&vdma2,&vdma3); - break; - } - default: { - return -1; - } - } - - if (V4L2_FIELD_HAS_BOTH(field)) { - } else if (field == V4L2_FIELD_ALTERNATE) { - /* fixme */ - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - vdma2.base_odd = vdma2.prot_addr; - vdma2.pitch /= 2; - vdma3.base_odd = vdma3.prot_addr; - vdma3.pitch /= 2; - } else if (field == V4L2_FIELD_TOP) { - vdma1.base_odd = vdma1.prot_addr; - vdma1.pitch /= 2; - vdma2.base_odd = vdma2.prot_addr; - vdma2.pitch /= 2; - vdma3.base_odd = vdma3.prot_addr; - vdma3.pitch /= 2; - } else if (field == V4L2_FIELD_BOTTOM) { - vdma1.base_odd = vdma1.base_even; - vdma1.base_even = vdma1.prot_addr; - vdma1.pitch /= 2; - vdma2.base_odd = vdma2.base_even; - vdma2.base_even = vdma2.prot_addr; - vdma2.pitch /= 2; - vdma3.base_odd = vdma3.base_even; - vdma3.base_even = vdma3.prot_addr; - vdma3.pitch /= 2; - } - - if( 0 != vv->vflip ) { - vdma1.pitch *= -1; - vdma2.pitch *= -1; - vdma3.pitch *= -1; - } - - saa7146_write_out_dma(dev, 1, &vdma1); - if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) { - saa7146_write_out_dma(dev, 3, &vdma2); - saa7146_write_out_dma(dev, 2, &vdma3); - } else { - saa7146_write_out_dma(dev, 2, &vdma2); - saa7146_write_out_dma(dev, 3, &vdma3); - } - return 0; -} - -static void program_capture_engine(struct saa7146_dev *dev, int planar) -{ - struct saa7146_vv *vv = dev->vv_data; - int count = 0; - - unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; - unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; - - /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/ - WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait); - WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait); - - /* set rps register 0 */ - WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4)); - WRITE_RPS0(MASK_27 | MASK_11); - - /* turn on video-dma1 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_06 | MASK_22); /* => mask */ - WRITE_RPS0(MASK_06 | MASK_22); /* => values */ - if( 0 != planar ) { - /* turn on video-dma2 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ - WRITE_RPS0(MASK_05 | MASK_21); /* => values */ - - /* turn on video-dma3 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ - WRITE_RPS0(MASK_04 | MASK_20); /* => values */ - } - - /* wait for o_fid_a/b / e_fid_a/b toggle */ - if ( vv->last_field == V4L2_FIELD_INTERLACED ) { - WRITE_RPS0(CMD_PAUSE | o_wait); - WRITE_RPS0(CMD_PAUSE | e_wait); - } else if ( vv->last_field == V4L2_FIELD_TOP ) { - WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); - WRITE_RPS0(CMD_PAUSE | o_wait); - } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { - WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); - WRITE_RPS0(CMD_PAUSE | e_wait); - } - - /* turn off video-dma1 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_22 | MASK_06); /* => mask */ - WRITE_RPS0(MASK_22); /* => values */ - if( 0 != planar ) { - /* turn off video-dma2 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ - WRITE_RPS0(MASK_21); /* => values */ - - /* turn off video-dma3 */ - WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ - WRITE_RPS0(MASK_20); /* => values */ - } - - /* generate interrupt */ - WRITE_RPS0(CMD_INTERRUPT); - - /* stop */ - WRITE_RPS0(CMD_STOP); -} - -void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) -{ - struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); - struct saa7146_vv *vv = dev->vv_data; - u32 vdma1_prot_addr; - - DEB_CAP("buf:%p, next:%p\n", buf, next); - - vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1); - if( 0 == vdma1_prot_addr ) { - /* clear out beginning of streaming bit (rps register 0)*/ - DEB_CAP("forcing sync to new frame\n"); - saa7146_write(dev, MC2, MASK_27 ); - } - - saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field); - saa7146_set_output_format(dev, sfmt->trans); - saa7146_disable_clipping(dev); - - if ( vv->last_field == V4L2_FIELD_INTERLACED ) { - } else if ( vv->last_field == V4L2_FIELD_TOP ) { - vv->last_field = V4L2_FIELD_BOTTOM; - } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { - vv->last_field = V4L2_FIELD_TOP; - } - - if( 0 != IS_PLANAR(sfmt->trans)) { - calculate_video_dma_grab_planar(dev, buf); - program_capture_engine(dev,1); - } else { - calculate_video_dma_grab_packed(dev, buf); - program_capture_engine(dev,0); - } - -/* - printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); - printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); - printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); - printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); - printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); - printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); - printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1)); -*/ - - /* write the address of the rps-program */ - saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); - - /* turn on rps */ - saa7146_write(dev, MC1, (MASK_12 | MASK_28)); -} diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c deleted file mode 100644 index df9ebe2a168c..000000000000 --- a/drivers/media/common/saa7146/saa7146_i2c.c +++ /dev/null @@ -1,421 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include - -static u32 saa7146_i2c_func(struct i2c_adapter *adapter) -{ - /* DEB_I2C("'%s'\n", adapter->name); */ - - return I2C_FUNC_I2C - | I2C_FUNC_SMBUS_QUICK - | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE - | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; -} - -/* this function returns the status-register of our i2c-device */ -static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) -{ - u32 iicsta = saa7146_read(dev, I2C_STATUS); - /* DEB_I2C("status: 0x%08x\n", iicsta); */ - return iicsta; -} - -/* this function runs through the i2c-messages and prepares the data to be - sent through the saa7146. have a look at the specifications p. 122 ff - to understand this. it returns the number of u32s to send, or -1 - in case of an error. */ -static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) -{ - int h1, h2; - int i, j, addr; - int mem = 0, op_count = 0; - - /* first determine size of needed memory */ - for(i = 0; i < num; i++) { - mem += m[i].len + 1; - } - - /* worst case: we need one u32 for three bytes to be send - plus one extra byte to address the device */ - mem = 1 + ((mem-1) / 3); - - /* we assume that op points to a memory of at least - * SAA7146_I2C_MEM bytes size. if we exceed this limit... - */ - if ((4 * mem) > SAA7146_I2C_MEM) { - /* DEB_I2C("cannot prepare i2c-message\n"); */ - return -ENOMEM; - } - - /* be careful: clear out the i2c-mem first */ - memset(op,0,sizeof(__le32)*mem); - - /* loop through all messages */ - for(i = 0; i < num; i++) { - - 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)); - op_count++; - - /* loop through all bytes of message i */ - for(j = 0; j < m[i].len; j++) { - /* insert the data bytes */ - h1 = op_count/3; h2 = op_count%3; - op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); - op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); - op_count++; - } - - } - - /* have a look at the last byte inserted: - if it was: ...CONT change it to ...STOP */ - h1 = (op_count-1)/3; h2 = (op_count-1)%3; - if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { - op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); - op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); - } - - /* return the number of u32s to send */ - return mem; -} - -/* this functions loops through all i2c-messages. normally, it should determine - which bytes were read through the adapter and write them back to the corresponding - i2c-message. but instead, we simply write back all bytes. - fixme: this could be improved. */ -static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) -{ - int i, j; - int op_count = 0; - - /* loop through all messages */ - for(i = 0; i < num; i++) { - - op_count++; - - /* loop through all bytes of message i */ - for(j = 0; j < m[i].len; j++) { - /* write back all bytes that could have been read */ - m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); - op_count++; - } - } - - return 0; -} - -/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ -static int saa7146_i2c_reset(struct saa7146_dev *dev) -{ - /* get current status */ - u32 status = saa7146_i2c_status(dev); - - /* clear registers for sure */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, 0); - - /* check if any operation is still in progress */ - if ( 0 != ( status & SAA7146_I2C_BUSY) ) { - - /* yes, kill ongoing operation */ - DEB_I2C("busy_state detected\n"); - - /* set "ABORT-OPERATION"-bit (bit 7)*/ - saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* clear all error-bits pending; this is needed because p.123, note 1 */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - } - - /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ - status = saa7146_i2c_status(dev); - - if ( dev->i2c_bitrate != status ) { - - DEB_I2C("error_state detected. status:0x%08x\n", status); - - /* Repeat the abort operation. This seems to be necessary - after serious protocol errors caused by e.g. the SAA7740 */ - saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* clear all error-bits pending */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* the data sheet says it might be necessary to clear the status - twice after an abort */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - } - - /* if any error is still present, a fatal error has occurred ... */ - status = saa7146_i2c_status(dev); - if ( dev->i2c_bitrate != status ) { - DEB_I2C("fatal error. status:0x%08x\n", status); - return -1; - } - - return 0; -} - -/* this functions writes out the data-byte 'dword' to the i2c-device. - it returns 0 if ok, -1 if the transfer failed, -2 if the transfer - failed badly (e.g. address error) */ -static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) -{ - u32 status = 0, mc2 = 0; - int trial = 0; - unsigned long timeout; - - /* write out i2c-command */ - DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n", - *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op); - - if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { - - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); - - dev->i2c_op = 1; - SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); - SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - - timeout = HZ/100 + 1; /* 10ms */ - timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); - if (timeout == -ERESTARTSYS || dev->i2c_op) { - SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); - SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); - if (timeout == -ERESTARTSYS) - /* a signal arrived */ - return -ERESTARTSYS; - - pr_warn("%s %s [irq]: timed out waiting for end of xfer\n", - dev->name, __func__); - return -EIO; - } - status = saa7146_read(dev, I2C_STATUS); - } else { - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - - /* do not poll for i2c-status before upload is complete */ - timeout = jiffies + HZ/100 + 1; /* 10ms */ - while(1) { - mc2 = (saa7146_read(dev, MC2) & 0x1); - if( 0 != mc2 ) { - break; - } - if (time_after(jiffies,timeout)) { - pr_warn("%s %s: timed out waiting for MC2\n", - dev->name, __func__); - return -EIO; - } - } - /* wait until we get a transfer done or error */ - timeout = jiffies + HZ/100 + 1; /* 10ms */ - /* first read usually delivers bogus results... */ - saa7146_i2c_status(dev); - while(1) { - status = saa7146_i2c_status(dev); - if ((status & 0x3) != 1) - break; - if (time_after(jiffies,timeout)) { - /* this is normal when probing the bus - * (no answer from nonexisistant device...) - */ - pr_warn("%s %s [poll]: timed out waiting for end of xfer\n", - dev->name, __func__); - return -EIO; - } - if (++trial < 50 && short_delay) - udelay(10); - else - msleep(1); - } - } - - /* give a detailed status report */ - if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR | - SAA7146_I2C_DTERR | SAA7146_I2C_DRERR | - SAA7146_I2C_AL | SAA7146_I2C_ERR | - SAA7146_I2C_BUSY)) ) { - - if ( 0 == (status & SAA7146_I2C_ERR) || - 0 == (status & SAA7146_I2C_BUSY) ) { - /* it may take some time until ERR goes high - ignore */ - DEB_I2C("unexpected i2c status %04x\n", status); - } - if( 0 != (status & SAA7146_I2C_SPERR) ) { - DEB_I2C("error due to invalid start/stop condition\n"); - } - if( 0 != (status & SAA7146_I2C_DTERR) ) { - DEB_I2C("error in data transmission\n"); - } - if( 0 != (status & SAA7146_I2C_DRERR) ) { - DEB_I2C("error when receiving data\n"); - } - if( 0 != (status & SAA7146_I2C_AL) ) { - DEB_I2C("error because arbitration lost\n"); - } - - /* we handle address-errors here */ - if( 0 != (status & SAA7146_I2C_APERR) ) { - DEB_I2C("error in address phase\n"); - return -EREMOTEIO; - } - - return -EIO; - } - - /* read back data, just in case we were reading ... */ - *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); - - DEB_I2C("after: 0x%08x\n", *dword); - return 0; -} - -static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) -{ - int i = 0, count = 0; - __le32 *buffer = dev->d_i2c.cpu_addr; - int err = 0; - int short_delay = 0; - - if (mutex_lock_interruptible(&dev->i2c_lock)) - return -ERESTARTSYS; - - for(i=0;i count ) { - err = -EIO; - goto out; - } - - if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) - short_delay = 1; - - do { - /* reset the i2c-device if necessary */ - err = saa7146_i2c_reset(dev); - if ( 0 > err ) { - DEB_I2C("could not reset i2c-device\n"); - goto out; - } - - /* write out the u32s one after another */ - for(i = 0; i < count; i++) { - err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); - if ( 0 != err) { - /* this one is unsatisfying: some i2c slaves on some - dvb cards don't acknowledge correctly, so the saa7146 - thinks that an address error occurred. in that case, the - transaction should be retrying, even if an address error - occurred. analog saa7146 based cards extensively rely on - i2c address probing, however, and address errors indicate that a - device is really *not* there. retrying in that case - increases the time the device needs to probe greatly, so - it should be avoided. So we bail out in irq mode after an - address error and trust the saa7146 address error detection. */ - if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) - goto out; - DEB_I2C("error while sending message(s). starting again\n"); - break; - } - } - if( 0 == err ) { - err = num; - break; - } - - /* delay a bit before retrying */ - msleep(10); - - } while (err != num && retries--); - - /* quit if any error occurred */ - if (err != num) - goto out; - - /* if any things had to be read, get the results */ - if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) { - DEB_I2C("could not cleanup i2c-message\n"); - err = -EIO; - goto out; - } - - /* return the number of delivered messages */ - DEB_I2C("transmission successful. (msg:%d)\n", err); -out: - /* another bug in revision 0: the i2c-registers get uploaded randomly by other - uploads, so we better clear them out before continuing */ - if( 0 == dev->revision ) { - __le32 zero = 0; - saa7146_i2c_reset(dev); - if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { - pr_info("revision 0 error. this should never happen\n"); - } - } - - mutex_unlock(&dev->i2c_lock); - return err; -} - -/* utility functions */ -static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) -{ - struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); - struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); - - /* use helper function to transfer data */ - return saa7146_i2c_transfer(dev, msg, num, adapter->retries); -} - - -/*****************************************************************************/ -/* i2c-adapter helper functions */ - -/* exported algorithm data */ -static const struct i2c_algorithm saa7146_algo = { - .master_xfer = saa7146_i2c_xfer, - .functionality = saa7146_i2c_func, -}; - -int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) -{ - DEB_EE("bitrate: 0x%08x\n", bitrate); - - /* enable i2c-port pins */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24)); - - dev->i2c_bitrate = bitrate; - saa7146_i2c_reset(dev); - - if (i2c_adapter) { - i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev); - i2c_adapter->dev.parent = &dev->pci->dev; - i2c_adapter->algo = &saa7146_algo; - i2c_adapter->algo_data = NULL; - i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; - i2c_adapter->retries = SAA7146_I2C_RETRIES; - } - - return 0; -} diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c deleted file mode 100644 index bd442b984423..000000000000 --- a/drivers/media/common/saa7146/saa7146_vbi.c +++ /dev/null @@ -1,498 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include - -static int vbi_pixel_to_capture = 720 * 2; - -static int vbi_workaround(struct saa7146_dev *dev) -{ - struct saa7146_vv *vv = dev->vv_data; - - u32 *cpu; - dma_addr_t dma_addr; - - int count = 0; - int i; - - DECLARE_WAITQUEUE(wait, current); - - DEB_VBI("dev:%p\n", dev); - - /* once again, a bug in the saa7146: the brs acquisition - is buggy and especially the BXO-counter does not work - as specified. there is this workaround, but please - don't let me explain it. ;-) */ - - cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL); - if (NULL == cpu) - return -ENOMEM; - - /* setup some basic programming, just for the workaround */ - saa7146_write(dev, BASE_EVEN3, dma_addr); - saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture); - saa7146_write(dev, PROT_ADDR3, dma_addr+4096); - saa7146_write(dev, PITCH3, vbi_pixel_to_capture); - saa7146_write(dev, BASE_PAGE3, 0x0); - saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0)); - saa7146_write(dev, MC2, MASK_04|MASK_20); - - /* load brs-control register */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); - /* BXO = 1h, BRS to outbound */ - WRITE_RPS1(0xc000008c); - /* wait for vbi_a or vbi_b*/ - if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { - DEB_D("...using port b\n"); - WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B); - WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B); -/* - WRITE_RPS1(CMD_PAUSE | MASK_09); -*/ - } else { - DEB_D("...using port a\n"); - WRITE_RPS1(CMD_PAUSE | MASK_10); - } - /* upload brs */ - WRITE_RPS1(CMD_UPLOAD | MASK_08); - /* load brs-control register */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); - /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */ - WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19); - /* wait for brs_done */ - WRITE_RPS1(CMD_PAUSE | MASK_08); - /* upload brs */ - WRITE_RPS1(CMD_UPLOAD | MASK_08); - /* load video-dma3 NumLines3 and NumBytes3 */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4)); - /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */ - WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture)); - /* load brs-control register */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); - /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */ - WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start - /* wait for brs_done */ - WRITE_RPS1(CMD_PAUSE | MASK_08); - /* upload brs and video-dma3*/ - WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04); - /* load mc2 register: enable dma3 */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4)); - WRITE_RPS1(MASK_20 | MASK_04); - /* generate interrupt */ - WRITE_RPS1(CMD_INTERRUPT); - /* stop rps1 */ - WRITE_RPS1(CMD_STOP); - - /* we have to do the workaround twice to be sure that - everything is ok */ - for(i = 0; i < 2; i++) { - - /* indicate to the irq handler that we do the workaround */ - saa7146_write(dev, MC2, MASK_31|MASK_15); - - saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0)); - saa7146_write(dev, MC2, MASK_04|MASK_20); - - /* enable rps1 irqs */ - SAA7146_IER_ENABLE(dev,MASK_28); - - /* prepare to wait to be woken up by the irq-handler */ - add_wait_queue(&vv->vbi_wq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* start rps1 to enable workaround */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - schedule(); - - DEB_VBI("brs bug workaround %d/1\n", i); - - remove_wait_queue(&vv->vbi_wq, &wait); - __set_current_state(TASK_RUNNING); - - /* disable rps1 irqs */ - SAA7146_IER_DISABLE(dev,MASK_28); - - /* stop video-dma3 */ - saa7146_write(dev, MC1, MASK_20); - - if(signal_pending(current)) { - - DEB_VBI("aborted (rps:0x%08x)\n", - saa7146_read(dev, RPS_ADDR1)); - - /* stop rps1 for sure */ - saa7146_write(dev, MC1, MASK_29); - - dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); - return -EINTR; - } - } - - dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); - return 0; -} - -static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) -{ - struct saa7146_vv *vv = dev->vv_data; - - struct saa7146_video_dma vdma3; - - int count = 0; - unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; - unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; - -/* - vdma3.base_even = 0xc8000000+2560*70; - vdma3.base_odd = 0xc8000000; - vdma3.prot_addr = 0xc8000000+2560*164; - vdma3.pitch = 2560; - vdma3.base_page = 0; - vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above! -*/ - vdma3.base_even = buf->pt[2].offset; - vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture; - vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture; - vdma3.pitch = vbi_pixel_to_capture; - vdma3.base_page = buf->pt[2].dma | ME1; - vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture; - - saa7146_write_out_dma(dev, 3, &vdma3); - - /* write beginning of rps-program */ - count = 0; - - /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */ - - /* we don't wait here for the first field anymore. this is different from the video - capture and might cause that the first buffer is only half filled (with only - one field). but since this is some sort of streaming data, this is not that negative. - but by doing this, we can use the whole engine from videobuf-dma-sg.c... */ - -/* - WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait); - WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait); -*/ - /* set bit 1 */ - WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4)); - WRITE_RPS1(MASK_28 | MASK_12); - - /* turn on video-dma3 */ - WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4)); - WRITE_RPS1(MASK_04 | MASK_20); /* => mask */ - WRITE_RPS1(MASK_04 | MASK_20); /* => values */ - - /* wait for o_fid_a/b / e_fid_a/b toggle */ - WRITE_RPS1(CMD_PAUSE | o_wait); - WRITE_RPS1(CMD_PAUSE | e_wait); - - /* generate interrupt */ - WRITE_RPS1(CMD_INTERRUPT); - - /* stop */ - WRITE_RPS1(CMD_STOP); - - /* enable rps1 irqs */ - SAA7146_IER_ENABLE(dev, MASK_28); - - /* write the address of the rps-program */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - - /* turn on rps */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); -} - -static int buffer_activate(struct saa7146_dev *dev, - struct saa7146_buf *buf, - struct saa7146_buf *next) -{ - struct saa7146_vv *vv = dev->vv_data; - buf->vb.state = VIDEOBUF_ACTIVE; - - DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); - saa7146_set_vbi_capture(dev,buf,next); - - mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); - return 0; -} - -static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - - int err = 0; - int lines, llength, size; - - lines = 16 * 2 ; /* 2 fields */ - llength = vbi_pixel_to_capture; - size = lines * llength; - - DEB_VBI("vb:%p\n", vb); - - if (0 != buf->vb.baddr && buf->vb.bsize < size) { - DEB_VBI("size mismatch\n"); - return -EINVAL; - } - - if (buf->vb.size != size) - saa7146_dma_free(dev,q,buf); - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - buf->vb.width = llength; - buf->vb.height = lines; - buf->vb.size = size; - buf->vb.field = field; // FIXME: check this - - saa7146_pgtable_free(dev->pci, &buf->pt[2]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); - - err = videobuf_iolock(q,&buf->vb, NULL); - if (err) - goto oops; - err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], - dma->sglist, dma->sglen); - if (0 != err) - return err; - } - buf->vb.state = VIDEOBUF_PREPARED; - buf->activate = buffer_activate; - - return 0; - - oops: - DEB_VBI("error out\n"); - saa7146_dma_free(dev,q,buf); - - return err; -} - -static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - int llength,lines; - - lines = 16 * 2 ; /* 2 fields */ - llength = vbi_pixel_to_capture; - - *size = lines * llength; - *count = 2; - - DEB_VBI("count:%d, size:%d\n", *count, *size); - - return 0; -} - -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - - DEB_VBI("vb:%p\n", vb); - saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); -} - -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - - DEB_VBI("vb:%p\n", vb); - saa7146_dma_free(dev,q,buf); -} - -static const struct videobuf_queue_ops vbi_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* ------------------------------------------------------------------ */ - -static void vbi_stop(struct saa7146_fh *fh, struct file *file) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - unsigned long flags; - DEB_VBI("dev:%p, fh:%p\n", dev, fh); - - spin_lock_irqsave(&dev->slock,flags); - - /* disable rps1 */ - saa7146_write(dev, MC1, MASK_29); - - /* disable rps1 irqs */ - SAA7146_IER_DISABLE(dev, MASK_28); - - /* shut down dma 3 transfers */ - saa7146_write(dev, MC1, MASK_20); - - if (vv->vbi_dmaq.curr) - saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); - - videobuf_queue_cancel(&fh->vbi_q); - - vv->vbi_streaming = NULL; - - del_timer(&vv->vbi_dmaq.timeout); - del_timer(&vv->vbi_read_timeout); - - spin_unlock_irqrestore(&dev->slock, flags); -} - -static void vbi_read_timeout(struct timer_list *t) -{ - struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); - struct file *file = vv->vbi_read_timeout_file; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - - DEB_VBI("dev:%p, fh:%p\n", dev, fh); - - vbi_stop(fh, file); -} - -static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) -{ - DEB_VBI("dev:%p\n", dev); - - INIT_LIST_HEAD(&vv->vbi_dmaq.queue); - - timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); - vv->vbi_dmaq.dev = dev; - - init_waitqueue_head(&vv->vbi_wq); -} - -static int vbi_open(struct saa7146_dev *dev, struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_vv *vv = fh->dev->vv_data; - - u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); - int ret = 0; - - DEB_VBI("dev:%p, fh:%p\n", dev, fh); - - ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); - if (0 == ret) { - DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); - return -EBUSY; - } - - /* adjust arbitrition control for video dma 3 */ - arbtr_ctrl &= ~0x1f0000; - arbtr_ctrl |= 0x1d0000; - saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); - saa7146_write(dev, MC2, (MASK_04|MASK_20)); - - videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, // FIXME: does this really work? - sizeof(struct saa7146_buf), - file, &dev->v4l2_lock); - - vv->vbi_read_timeout.function = vbi_read_timeout; - vv->vbi_read_timeout_file = file; - - /* initialize the brs */ - if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { - saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19)); - } else { - saa7146_write(dev, BRS_CTRL, 0x00000001); - - if (0 != (ret = vbi_workaround(dev))) { - DEB_VBI("vbi workaround failed!\n"); - /* return ret;*/ - } - } - - /* upload brs register */ - saa7146_write(dev, MC2, (MASK_08|MASK_24)); - return 0; -} - -static void vbi_close(struct saa7146_dev *dev, struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_vv *vv = dev->vv_data; - DEB_VBI("dev:%p, fh:%p\n", dev, fh); - - if( fh == vv->vbi_streaming ) { - vbi_stop(fh, file); - } - saa7146_res_free(fh, RESOURCE_DMA3_BRS); -} - -static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) -{ - struct saa7146_vv *vv = dev->vv_data; - spin_lock(&dev->slock); - - if (vv->vbi_dmaq.curr) { - DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); - /* this must be += 2, one count for each field */ - vv->vbi_fieldcount+=2; - vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; - saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); - } else { - DEB_VBI("dev:%p\n", dev); - } - saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); - - spin_unlock(&dev->slock); -} - -static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - ssize_t ret = 0; - - DEB_VBI("dev:%p, fh:%p\n", dev, fh); - - if( NULL == vv->vbi_streaming ) { - // fixme: check if dma3 is available - // fixme: activate vbi engine here if necessary. (really?) - vv->vbi_streaming = fh; - } - - if( fh != vv->vbi_streaming ) { - DEB_VBI("open %p is already using vbi capture\n", - vv->vbi_streaming); - return -EBUSY; - } - - mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); - ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, - file->f_flags & O_NONBLOCK); -/* - printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); - printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); - printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); - printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); - printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); - printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); - printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); -*/ - return ret; -} - -const struct saa7146_use_ops saa7146_vbi_uops = { - .init = vbi_init, - .open = vbi_open, - .release = vbi_close, - .irq_done = vbi_irq_done, - .read = vbi_read, -}; diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c deleted file mode 100644 index 2296765079a4..000000000000 --- a/drivers/media/common/saa7146/saa7146_video.c +++ /dev/null @@ -1,1286 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -static int max_memory = 32; - -module_param(max_memory, int, 0644); -MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); - -#define IS_CAPTURE_ACTIVE(fh) \ - (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) - -#define IS_OVERLAY_ACTIVE(fh) \ - (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) - -/* format descriptions for capture and preview */ -static struct saa7146_format formats[] = { - { - .pixelformat = V4L2_PIX_FMT_RGB332, - .trans = RGB08_COMPOSED, - .depth = 8, - .flags = 0, - }, { - .pixelformat = V4L2_PIX_FMT_RGB565, - .trans = RGB16_COMPOSED, - .depth = 16, - .flags = 0, - }, { - .pixelformat = V4L2_PIX_FMT_BGR24, - .trans = RGB24_COMPOSED, - .depth = 24, - .flags = 0, - }, { - .pixelformat = V4L2_PIX_FMT_BGR32, - .trans = RGB32_COMPOSED, - .depth = 32, - .flags = 0, - }, { - .pixelformat = V4L2_PIX_FMT_RGB32, - .trans = RGB32_COMPOSED, - .depth = 32, - .flags = 0, - .swap = 0x2, - }, { - .pixelformat = V4L2_PIX_FMT_GREY, - .trans = Y8, - .depth = 8, - .flags = 0, - }, { - .pixelformat = V4L2_PIX_FMT_YUV422P, - .trans = YUV422_DECOMPOSED, - .depth = 16, - .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, - }, { - .pixelformat = V4L2_PIX_FMT_YVU420, - .trans = YUV420_DECOMPOSED, - .depth = 12, - .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, - }, { - .pixelformat = V4L2_PIX_FMT_YUV420, - .trans = YUV420_DECOMPOSED, - .depth = 12, - .flags = FORMAT_IS_PLANAR, - }, { - .pixelformat = V4L2_PIX_FMT_UYVY, - .trans = YUV422_COMPOSED, - .depth = 16, - .flags = 0, - } -}; - -/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. - due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped - (like V4L2_PIX_FMT_YUYV) ... 8-( */ - -struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].pixelformat == fourcc) { - return formats+i; - } - } - - DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc); - return NULL; -} - -static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f); - -int saa7146_start_preview(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct v4l2_format fmt; - int ret = 0, err = 0; - - DEB_EE("dev:%p, fh:%p\n", dev, fh); - - /* check if we have overlay information */ - if (vv->ov.fh == NULL) { - DEB_D("no overlay data available. try S_FMT first.\n"); - return -EAGAIN; - } - - /* check if streaming capture is running */ - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("streaming capture is active\n"); - return -EBUSY; - } - - /* check if overlay is running */ - if (IS_OVERLAY_ACTIVE(fh) != 0) { - if (vv->video_fh == fh) { - DEB_D("overlay is already active\n"); - return 0; - } - DEB_D("overlay is already active in another open\n"); - return -EBUSY; - } - - if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { - DEB_D("cannot get necessary overlay resources\n"); - return -EBUSY; - } - - fmt.fmt.win = vv->ov.win; - err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); - if (0 != err) { - saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); - return -EBUSY; - } - vv->ov.win = fmt.fmt.win; - - DEB_D("%dx%d+%d+%d 0x%08x field=%s\n", - vv->ov.win.w.width, vv->ov.win.w.height, - vv->ov.win.w.left, vv->ov.win.w.top, - vv->ov_fmt->pixelformat, v4l2_field_names[vv->ov.win.field]); - - if (0 != (ret = saa7146_enable_overlay(fh))) { - DEB_D("enabling overlay failed: %d\n", ret); - saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); - return ret; - } - - vv->video_status = STATUS_OVERLAY; - vv->video_fh = fh; - - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_start_preview); - -int saa7146_stop_preview(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - - DEB_EE("dev:%p, fh:%p\n", dev, fh); - - /* check if streaming capture is running */ - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("streaming capture is active\n"); - return -EBUSY; - } - - /* check if overlay is running at all */ - if ((vv->video_status & STATUS_OVERLAY) == 0) { - DEB_D("no active overlay\n"); - return 0; - } - - if (vv->video_fh != fh) { - DEB_D("overlay is active, but in another open\n"); - return -EBUSY; - } - - vv->video_status = 0; - vv->video_fh = NULL; - - saa7146_disable_overlay(fh); - - saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); - - return 0; -} -EXPORT_SYMBOL_GPL(saa7146_stop_preview); - -/********************************************************************************/ -/* common pagetable functions */ - -static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) -{ - struct pci_dev *pci = dev->pci; - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - struct scatterlist *list = dma->sglist; - int length = dma->sglen; - struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); - - DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); - - if( 0 != IS_PLANAR(sfmt->trans)) { - struct saa7146_pgtable *pt1 = &buf->pt[0]; - struct saa7146_pgtable *pt2 = &buf->pt[1]; - struct saa7146_pgtable *pt3 = &buf->pt[2]; - __le32 *ptr1, *ptr2, *ptr3; - __le32 fill; - - int size = buf->fmt->width*buf->fmt->height; - int i,p,m1,m2,m3,o1,o2; - - switch( sfmt->depth ) { - case 12: { - /* create some offsets inside the page table */ - m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; - m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; - m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; - o1 = size%PAGE_SIZE; - o2 = (size+(size/4))%PAGE_SIZE; - DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", - size, m1, m2, m3, o1, o2); - break; - } - case 16: { - /* create some offsets inside the page table */ - m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; - m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; - m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; - o1 = size%PAGE_SIZE; - o2 = (size+(size/2))%PAGE_SIZE; - DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", - size, m1, m2, m3, o1, o2); - break; - } - default: { - return -1; - } - } - - ptr1 = pt1->cpu; - ptr2 = pt2->cpu; - ptr3 = pt3->cpu; - - /* walk all pages, copy all page addresses to ptr1 */ - for (i = 0; i < length; i++, list++) { - for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr1++) - *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); - } -/* - ptr1 = pt1->cpu; - for(j=0;j<40;j++) { - printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); - } -*/ - - /* if we have a user buffer, the first page may not be - aligned to a page boundary. */ - pt1->offset = dma->sglist->offset; - pt2->offset = pt1->offset+o1; - pt3->offset = pt1->offset+o2; - - /* create video-dma2 page table */ - ptr1 = pt1->cpu; - for(i = m1; i <= m2 ; i++, ptr2++) { - *ptr2 = ptr1[i]; - } - fill = *(ptr2-1); - for(;i<1024;i++,ptr2++) { - *ptr2 = fill; - } - /* create video-dma3 page table */ - ptr1 = pt1->cpu; - for(i = m2; i <= m3; i++,ptr3++) { - *ptr3 = ptr1[i]; - } - fill = *(ptr3-1); - for(;i<1024;i++,ptr3++) { - *ptr3 = fill; - } - /* finally: finish up video-dma1 page table */ - ptr1 = pt1->cpu+m1; - fill = pt1->cpu[m1]; - for(i=m1;i<1024;i++,ptr1++) { - *ptr1 = fill; - } -/* - ptr1 = pt1->cpu; - ptr2 = pt2->cpu; - ptr3 = pt3->cpu; - for(j=0;j<40;j++) { - printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); - } - for(j=0;j<40;j++) { - printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); - } - for(j=0;j<40;j++) { - printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); - } -*/ - } else { - struct saa7146_pgtable *pt = &buf->pt[0]; - return saa7146_pgtable_build_single(pci, pt, list, length); - } - - return 0; -} - - -/********************************************************************************/ -/* file operations */ - -static int video_begin(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_format *fmt = NULL; - unsigned int resource; - int ret = 0, err = 0; - - DEB_EE("dev:%p, fh:%p\n", dev, fh); - - if ((vv->video_status & STATUS_CAPTURE) != 0) { - if (vv->video_fh == fh) { - DEB_S("already capturing\n"); - return 0; - } - DEB_S("already capturing in another open\n"); - return -EBUSY; - } - - if ((vv->video_status & STATUS_OVERLAY) != 0) { - DEB_S("warning: suspending overlay video for streaming capture\n"); - vv->ov_suspend = vv->video_fh; - err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ - if (0 != err) { - DEB_D("suspending video failed. aborting\n"); - return err; - } - } - - fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); - /* we need to have a valid format set here */ - if (!fmt) - return -EINVAL; - - if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { - resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; - } else { - resource = RESOURCE_DMA1_HPS; - } - - ret = saa7146_res_get(fh, resource); - if (0 == ret) { - DEB_S("cannot get capture resource %d\n", resource); - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - return -EBUSY; - } - - /* clear out beginning of streaming bit (rps register 0)*/ - saa7146_write(dev, MC2, MASK_27 ); - - /* enable rps0 irqs */ - SAA7146_IER_ENABLE(dev, MASK_27); - - vv->video_fh = fh; - vv->video_status = STATUS_CAPTURE; - - return 0; -} - -static int video_end(struct saa7146_fh *fh, struct file *file) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_dmaqueue *q = &vv->video_dmaq; - struct saa7146_format *fmt = NULL; - unsigned long flags; - unsigned int resource; - u32 dmas = 0; - DEB_EE("dev:%p, fh:%p\n", dev, fh); - - if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { - DEB_S("not capturing\n"); - return 0; - } - - if (vv->video_fh != fh) { - DEB_S("capturing, but in another open\n"); - return -EBUSY; - } - - fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); - /* we need to have a valid format set here */ - if (!fmt) - return -EINVAL; - - if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { - resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; - dmas = MASK_22 | MASK_21 | MASK_20; - } else { - resource = RESOURCE_DMA1_HPS; - dmas = MASK_22; - } - spin_lock_irqsave(&dev->slock,flags); - - /* disable rps0 */ - saa7146_write(dev, MC1, MASK_28); - - /* disable rps0 irqs */ - SAA7146_IER_DISABLE(dev, MASK_27); - - /* shut down all used video dma transfers */ - saa7146_write(dev, MC1, dmas); - - if (q->curr) - saa7146_buffer_finish(dev, q, VIDEOBUF_DONE); - - spin_unlock_irqrestore(&dev->slock, flags); - - vv->video_fh = NULL; - vv->video_status = 0; - - saa7146_res_free(fh, resource); - - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - - return 0; -} - -static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - - strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver)); - strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | - V4L2_CAP_DEVICE_CAPS; - cap->capabilities |= dev->ext_vv_data->capabilities; - return 0; -} - -static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - *fb = vv->ov_fb; - fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - fb->flags = V4L2_FBUF_FLAG_PRIMARY; - return 0; -} - -static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_format *fmt; - - DEB_EE("VIDIOC_S_FBUF\n"); - - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) - return -EPERM; - - /* check args */ - fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); - if (NULL == fmt) - return -EINVAL; - - /* planar formats are not allowed for overlay video, clipping and video dma would clash */ - if (fmt->flags & FORMAT_IS_PLANAR) - DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", - (char *)&fmt->pixelformat); - - /* check if overlay is running */ - if (IS_OVERLAY_ACTIVE(fh) != 0) { - if (vv->video_fh != fh) { - DEB_D("refusing to change framebuffer information while overlay is active in another open\n"); - return -EBUSY; - } - } - - /* ok, accept it */ - vv->ov_fb = *fb; - vv->ov_fmt = fmt; - - if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { - vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; - DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); - } - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - f->pixelformat = formats[f->index].pixelformat; - return 0; -} - -int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct saa7146_dev *dev = container_of(ctrl->handler, - struct saa7146_dev, ctrl_handler); - struct saa7146_vv *vv = dev->vv_data; - u32 val; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - val = saa7146_read(dev, BCS_CTRL); - val &= 0x00ffffff; - val |= (ctrl->val << 24); - saa7146_write(dev, BCS_CTRL, val); - saa7146_write(dev, MC2, MASK_22 | MASK_06); - break; - - case V4L2_CID_CONTRAST: - val = saa7146_read(dev, BCS_CTRL); - val &= 0xff00ffff; - val |= (ctrl->val << 16); - saa7146_write(dev, BCS_CTRL, val); - saa7146_write(dev, MC2, MASK_22 | MASK_06); - break; - - case V4L2_CID_SATURATION: - val = saa7146_read(dev, BCS_CTRL); - val &= 0xffffff00; - val |= (ctrl->val << 0); - saa7146_write(dev, BCS_CTRL, val); - saa7146_write(dev, MC2, MASK_22 | MASK_06); - break; - - case V4L2_CID_HFLIP: - /* fixme: we can support changing VFLIP and HFLIP here... */ - if ((vv->video_status & STATUS_CAPTURE)) - return -EBUSY; - vv->hflip = ctrl->val; - break; - - case V4L2_CID_VFLIP: - if ((vv->video_status & STATUS_CAPTURE)) - return -EBUSY; - vv->vflip = ctrl->val; - break; - - default: - return -EINVAL; - } - - if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ - struct saa7146_fh *fh = vv->video_fh; - - saa7146_stop_preview(fh); - saa7146_start_preview(fh); - } - return 0; -} - -static int vidioc_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - parm->parm.capture.readbuffers = 1; - v4l2_video_std_frame_period(vv->standard->id, - &parm->parm.capture.timeperframe); - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - f->fmt.pix = vv->video_fmt; - return 0; -} - -static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - f->fmt.win = vv->ov.win; - return 0; -} - -static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - f->fmt.vbi = vv->vbi_fmt; - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_format *fmt; - enum v4l2_field field; - int maxw, maxh; - int calc_bpl; - - DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); - - fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; - - field = f->fmt.pix.field; - maxw = vv->standard->h_max_out; - maxh = vv->standard->v_max_out; - - if (V4L2_FIELD_ANY == field) { - field = (f->fmt.pix.height > maxh / 2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_BOTTOM; - } - switch (field) { - case V4L2_FIELD_ALTERNATE: - vv->last_field = V4L2_FIELD_TOP; - maxh = maxh / 2; - break; - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - vv->last_field = V4L2_FIELD_INTERLACED; - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - vv->last_field = V4L2_FIELD_INTERLACED; - break; - default: - DEB_D("no known field mode '%d'\n", field); - return -EINVAL; - } - - f->fmt.pix.field = field; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - - calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; - - if (f->fmt.pix.bytesperline < calc_bpl) - f->fmt.pix.bytesperline = calc_bpl; - - if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ - f->fmt.pix.bytesperline = calc_bpl; - - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; - DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - - -static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - struct v4l2_window *win = &f->fmt.win; - enum v4l2_field field; - int maxw, maxh; - - DEB_EE("dev:%p\n", dev); - - if (NULL == vv->ov_fb.base) { - DEB_D("no fb base set\n"); - return -EINVAL; - } - if (NULL == vv->ov_fmt) { - DEB_D("no fb fmt set\n"); - return -EINVAL; - } - if (win->w.width < 48 || win->w.height < 32) { - DEB_D("min width/height. (%d,%d)\n", - win->w.width, win->w.height); - return -EINVAL; - } - if (win->clipcount > 16) { - DEB_D("clipcount too big\n"); - return -EINVAL; - } - - field = win->field; - maxw = vv->standard->h_max_out; - maxh = vv->standard->v_max_out; - - if (V4L2_FIELD_ANY == field) { - field = (win->w.height > maxh / 2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_TOP; - } - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_ALTERNATE: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - DEB_D("no known field mode '%d'\n", field); - return -EINVAL; - } - - win->field = field; - if (win->w.width > maxw) - win->w.width = maxw; - if (win->w.height > maxh) - win->w.height = maxh; - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) -{ - struct saa7146_fh *fh = __fh; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - int err; - - DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_EE("streaming capture is active\n"); - return -EBUSY; - } - err = vidioc_try_fmt_vid_cap(file, fh, f); - if (0 != err) - return err; - vv->video_fmt = f->fmt.pix; - DEB_EE("set to pixelformat '%4.4s'\n", - (char *)&vv->video_fmt.pixelformat); - return 0; -} - -static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) -{ - struct saa7146_fh *fh = __fh; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - int err; - - DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); - err = vidioc_try_fmt_vid_overlay(file, fh, f); - if (0 != err) - return err; - vv->ov.win = f->fmt.win; - vv->ov.nclips = f->fmt.win.clipcount; - if (vv->ov.nclips > 16) - vv->ov.nclips = 16; - memcpy(vv->ov.clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * vv->ov.nclips); - - /* vv->ov.fh is used to indicate that we have valid overlay information, too */ - vv->ov.fh = fh; - - /* check if our current overlay is active */ - if (IS_OVERLAY_ACTIVE(fh) != 0) { - saa7146_stop_preview(fh); - saa7146_start_preview(fh); - } - return 0; -} - -static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - - *norm = vv->standard->id; - return 0; -} - - /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) - PAL / NTSC / SECAM. if your hardware does not (or does more) - -- override this function in your extension */ -/* - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - if (e->index < 0 ) - return -EINVAL; - if( e->index < dev->ext_vv_data->num_stds ) { - DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); - v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); - return 0; - } - return -EINVAL; - } - */ - -static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - int found = 0; - int err, i; - - DEB_EE("VIDIOC_S_STD\n"); - - if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { - DEB_D("cannot change video standard while streaming capture is active\n"); - return -EBUSY; - } - - if ((vv->video_status & STATUS_OVERLAY) != 0) { - vv->ov_suspend = vv->video_fh; - err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ - if (0 != err) { - DEB_D("suspending video failed. aborting\n"); - return err; - } - } - - for (i = 0; i < dev->ext_vv_data->num_stds; i++) - if (id & dev->ext_vv_data->stds[i].id) - break; - if (i != dev->ext_vv_data->num_stds) { - vv->standard = &dev->ext_vv_data->stds[i]; - if (NULL != dev->ext_vv_data->std_callback) - dev->ext_vv_data->std_callback(dev, vv->standard); - found = 1; - } - - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - - if (!found) { - DEB_EE("VIDIOC_S_STD: standard not found\n"); - return -EINVAL; - } - - DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name); - return 0; -} - -static int vidioc_overlay(struct file *file, void *fh, unsigned int on) -{ - int err; - - DEB_D("VIDIOC_OVERLAY on:%d\n", on); - if (on) - err = saa7146_start_preview(fh); - else - err = saa7146_stop_preview(fh); - return err; -} - -static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) -{ - struct saa7146_fh *fh = __fh; - - if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_reqbufs(&fh->video_q, b); - if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return videobuf_reqbufs(&fh->vbi_q, b); - return -EINVAL; -} - -static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct saa7146_fh *fh = __fh; - - if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_querybuf(&fh->video_q, buf); - if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return videobuf_querybuf(&fh->vbi_q, buf); - return -EINVAL; -} - -static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct saa7146_fh *fh = __fh; - - if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_qbuf(&fh->video_q, buf); - if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return videobuf_qbuf(&fh->vbi_q, buf); - return -EINVAL; -} - -static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct saa7146_fh *fh = __fh; - - if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); - if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); - return -EINVAL; -} - -static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) -{ - struct saa7146_fh *fh = __fh; - int err; - - DEB_D("VIDIOC_STREAMON, type:%d\n", type); - - err = video_begin(fh); - if (err) - return err; - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_streamon(&fh->video_q); - if (type == V4L2_BUF_TYPE_VBI_CAPTURE) - return videobuf_streamon(&fh->vbi_q); - return -EINVAL; -} - -static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) -{ - struct saa7146_fh *fh = __fh; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - int err; - - DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); - - /* ugly: we need to copy some checks from video_end(), - because videobuf_streamoff() relies on the capture running. - check and fix this */ - if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { - DEB_S("not capturing\n"); - return 0; - } - - if (vv->video_fh != fh) { - DEB_S("capturing, but in another open\n"); - return -EBUSY; - } - - err = -EINVAL; - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - err = videobuf_streamoff(&fh->video_q); - else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) - err = videobuf_streamoff(&fh->vbi_q); - if (0 != err) { - DEB_D("warning: videobuf_streamoff() failed\n"); - video_end(fh, file); - } else { - err = video_end(fh, file); - } - return err; -} - -const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - - .vidioc_overlay = vidioc_overlay, - .vidioc_g_fbuf = vidioc_g_fbuf, - .vidioc_s_fbuf = vidioc_s_fbuf, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/*********************************************************************************/ -/* buffer handling functions */ - -static int buffer_activate (struct saa7146_dev *dev, - struct saa7146_buf *buf, - struct saa7146_buf *next) -{ - struct saa7146_vv *vv = dev->vv_data; - - buf->vb.state = VIDEOBUF_ACTIVE; - saa7146_set_capture(dev,buf,next); - - mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); - return 0; -} - -static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) -{ - saa7146_pgtable_free(dev->pci, &buf->pt[0]); - saa7146_pgtable_free(dev->pci, &buf->pt[1]); - saa7146_pgtable_free(dev->pci, &buf->pt[2]); -} - -static int buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - int size,err = 0; - - DEB_CAP("vbuf:%p\n", vb); - - /* sanity checks */ - if (vv->video_fmt.width < 48 || - vv->video_fmt.height < 32 || - vv->video_fmt.width > vv->standard->h_max_out || - vv->video_fmt.height > vv->standard->v_max_out) { - DEB_D("w (%d) / h (%d) out of bounds\n", - vv->video_fmt.width, vv->video_fmt.height); - return -EINVAL; - } - - size = vv->video_fmt.sizeimage; - if (0 != buf->vb.baddr && buf->vb.bsize < size) { - DEB_D("size mismatch\n"); - return -EINVAL; - } - - DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", - vv->video_fmt.width, vv->video_fmt.height, - size, v4l2_field_names[vv->video_fmt.field]); - if (buf->vb.width != vv->video_fmt.width || - buf->vb.bytesperline != vv->video_fmt.bytesperline || - buf->vb.height != vv->video_fmt.height || - buf->vb.size != size || - buf->vb.field != field || - buf->vb.field != vv->video_fmt.field || - buf->fmt != &vv->video_fmt) { - saa7146_dma_free(dev,q,buf); - } - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - struct saa7146_format *sfmt; - - buf->vb.bytesperline = vv->video_fmt.bytesperline; - buf->vb.width = vv->video_fmt.width; - buf->vb.height = vv->video_fmt.height; - buf->vb.size = size; - buf->vb.field = field; - buf->fmt = &vv->video_fmt; - buf->vb.field = vv->video_fmt.field; - - sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); - - release_all_pagetables(dev, buf); - if( 0 != IS_PLANAR(sfmt->trans)) { - saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); - } else { - saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); - } - - err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); - if (err) - goto oops; - err = saa7146_pgtable_build(dev,buf); - if (err) - goto oops; - } - buf->vb.state = VIDEOBUF_PREPARED; - buf->activate = buffer_activate; - - return 0; - - oops: - DEB_D("error out\n"); - saa7146_dma_free(dev,q,buf); - - return err; -} - -static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_vv *vv = fh->dev->vv_data; - - if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) - *count = MAX_SAA7146_CAPTURE_BUFFERS; - - *size = vv->video_fmt.sizeimage; - - /* check if we exceed the "max_memory" parameter */ - if( (*count * *size) > (max_memory*1048576) ) { - *count = (max_memory*1048576) / *size; - } - - DEB_CAP("%d buffers, %d bytes each\n", *count, *size); - - return 0; -} - -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - - DEB_CAP("vbuf:%p\n", vb); - saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); -} - -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct file *file = q->priv_data; - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_buf *buf = (struct saa7146_buf *)vb; - - DEB_CAP("vbuf:%p\n", vb); - - saa7146_dma_free(dev,q,buf); - - release_all_pagetables(dev, buf); -} - -static const struct videobuf_queue_ops video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************************************************************************/ -/* file operations */ - -static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) -{ - INIT_LIST_HEAD(&vv->video_dmaq.queue); - - timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); - vv->video_dmaq.dev = dev; - - /* set some default values */ - vv->standard = &dev->ext_vv_data->stds[0]; - - /* FIXME: what's this? */ - vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; - vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; -} - - -static int video_open(struct saa7146_dev *dev, struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - - videobuf_queue_sg_init(&fh->video_q, &video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct saa7146_buf), - file, &dev->v4l2_lock); - - return 0; -} - - -static void video_close(struct saa7146_dev *dev, struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_vv *vv = dev->vv_data; - struct videobuf_queue *q = &fh->video_q; - - if (IS_CAPTURE_ACTIVE(fh) != 0) - video_end(fh, file); - else if (IS_OVERLAY_ACTIVE(fh) != 0) - saa7146_stop_preview(fh); - - videobuf_stop(q); - /* hmm, why is this function declared void? */ -} - - -static void video_irq_done(struct saa7146_dev *dev, unsigned long st) -{ - struct saa7146_vv *vv = dev->vv_data; - struct saa7146_dmaqueue *q = &vv->video_dmaq; - - spin_lock(&dev->slock); - DEB_CAP("called\n"); - - /* only finish the buffer if we have one... */ - if( NULL != q->curr ) { - saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); - } - saa7146_buffer_next(dev,q,0); - - spin_unlock(&dev->slock); -} - -static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - ssize_t ret = 0; - - DEB_EE("called\n"); - - if ((vv->video_status & STATUS_CAPTURE) != 0) { - /* fixme: should we allow read() captures while streaming capture? */ - if (vv->video_fh == fh) { - DEB_S("already capturing\n"); - return -EBUSY; - } - DEB_S("already capturing in another open\n"); - return -EBUSY; - } - - ret = video_begin(fh); - if( 0 != ret) { - goto out; - } - - ret = videobuf_read_one(&fh->video_q , data, count, ppos, - file->f_flags & O_NONBLOCK); - if (ret != 0) { - video_end(fh, file); - } else { - ret = video_end(fh, file); - } -out: - /* restart overlay if it was active before */ - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - - return ret; -} - -const struct saa7146_use_ops saa7146_video_uops = { - .init = video_init, - .open = video_open, - .release = video_close, - .irq_done = video_irq_done, - .read = video_read, -}; diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 480194543d05..dff0b450f387 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -27,7 +27,6 @@ if MEDIA_ANALOG_TV_SUPPORT source "drivers/media/pci/dt3155/Kconfig" source "drivers/media/pci/ivtv/Kconfig" -source "drivers/media/pci/saa7146/Kconfig" endif @@ -58,7 +57,6 @@ source "drivers/media/pci/pluto2/Kconfig" source "drivers/media/pci/pt1/Kconfig" source "drivers/media/pci/pt3/Kconfig" source "drivers/media/pci/smipcie/Kconfig" -source "drivers/media/pci/ttpci/Kconfig" endif diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 8bed619b7130..8f887a8a7f17 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -5,8 +5,7 @@ # Please keep it alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) -obj-y += ttpci/ \ - b2c2/ \ +obj-y += b2c2/ \ pluto2/ \ dm1105/ \ pt1/ \ @@ -14,7 +13,6 @@ obj-y += ttpci/ \ mantis/ \ ngene/ \ ddbridge/ \ - saa7146/ \ smipcie/ \ netup_unidvb/ \ intel/ diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig deleted file mode 100644 index 3bbb68a0ed7b..000000000000 --- a/drivers/media/pci/saa7146/Kconfig +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_HEXIUM_GEMINI - tristate "Hexium Gemini frame grabber" - depends on PCI && VIDEO_DEV && I2C - select VIDEO_SAA7146_VV - help - This is a video4linux driver for the Hexium Gemini frame - grabber card by Hexium. Please note that the Gemini Dual - card is *not* fully supported. - - To compile this driver as a module, choose M here: the - module will be called hexium_gemini. - -config VIDEO_HEXIUM_ORION - tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on PCI && VIDEO_DEV && I2C - select VIDEO_SAA7146_VV - help - This is a video4linux driver for the Hexium HV-PCI6 and - Orion frame grabber cards by Hexium. - - To compile this driver as a module, choose M here: the - module will be called hexium_orion. - -config VIDEO_MXB - tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" - depends on PCI && VIDEO_DEV && I2C - select VIDEO_SAA7146_VV - select VIDEO_TUNER - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT - help - This is a video4linux driver for the 'Multimedia eXtension Board' - TV card by Siemens-Nixdorf. - - To compile this driver as a module, choose M here: the - module will be called mxb. diff --git a/drivers/media/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile deleted file mode 100644 index 37c9336f83d5..000000000000 --- a/drivers/media/pci/saa7146/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_MXB) += mxb.o -obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o -obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o - -ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c deleted file mode 100644 index 3947701cd6c7..000000000000 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ /dev/null @@ -1,425 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards - - Visit http://www.mihu.de/linux/saa7146/ and follow the link - to "hexium" for further details about this card. - - Copyright (C) 2003 Michael Hunold - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DEBUG_VARIABLE debug - -#include -#include -#include - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "debug verbosity"); - -/* global variables */ -static int hexium_num; - -#define HEXIUM_GEMINI 4 -#define HEXIUM_GEMINI_DUAL 5 - -#define HEXIUM_INPUTS 9 -static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, -}; - -#define HEXIUM_AUDIOS 0 - -struct hexium_data -{ - s8 adr; - u8 byte; -}; - -#define HEXIUM_GEMINI_V_1_0 1 -#define HEXIUM_GEMINI_DUAL_V_1_0 2 - -struct hexium -{ - int type; - - struct video_device video_dev; - struct i2c_adapter i2c_adapter; - - int cur_input; /* current input */ - v4l2_std_id cur_std; /* current standard */ -}; - -/* Samsung KS0127B decoder default registers */ -static u8 hexium_ks0127b[0x100]={ -/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10, -/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06, -/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00, -/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22, -/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00, -/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80, -/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00, -/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - -static struct hexium_data hexium_pal[] = { - { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } -}; - -static struct hexium_data hexium_ntsc[] = { - { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } -}; - -static struct hexium_data hexium_secam[] = { - { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } -}; - -static struct hexium_data hexium_input_select[] = { - { 0x02, 0x60 }, - { 0x02, 0x64 }, - { 0x02, 0x61 }, - { 0x02, 0x65 }, - { 0x02, 0x62 }, - { 0x02, 0x66 }, - { 0x02, 0x68 }, - { 0x02, 0x69 }, - { 0x02, 0x6A }, -}; - -/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which - are currently *not* supported*/ -static struct saa7146_standard hexium_standards[] = { - { - .name = "PAL", .id = V4L2_STD_PAL, - .v_offset = 28, .v_field = 288, - .h_offset = 1, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 28, .v_field = 240, - .h_offset = 1, .h_pixels = 640, - .v_max_out = 480, .h_max_out = 640, - }, { - .name = "SECAM", .id = V4L2_STD_SECAM, - .v_offset = 28, .v_field = 288, - .h_offset = 1, .h_pixels = 720, - .v_max_out = 576, .h_max_out = 768, - } -}; - -/* bring hardware to a sane state. this has to be done, just in case someone - wants to capture from this device before it has been properly initialized. - the capture engine would badly fail, because no valid signal arrives on the - saa7146, thus leading to timeouts and stuff. */ -static int hexium_init_done(struct saa7146_dev *dev) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - union i2c_smbus_data data; - int i = 0; - - DEB_D("hexium_init_done called\n"); - - /* initialize the helper ics to useful values */ - for (i = 0; i < sizeof(hexium_ks0127b); i++) { - data.byte = hexium_ks0127b[i]; - if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { - pr_err("hexium_init_done() failed for address 0x%02x\n", - i); - } - } - - return 0; -} - -static int hexium_set_input(struct hexium *hexium, int input) -{ - union i2c_smbus_data data; - - DEB_D("\n"); - - data.byte = hexium_input_select[input].byte; - if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) { - return -1; - } - - return 0; -} - -static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) -{ - union i2c_smbus_data data; - int i = 0; - - DEB_D("\n"); - - while (vdec[i].adr != -1) { - data.byte = vdec[i].byte; - if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) { - pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", - i); - return -1; - } - i++; - } - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); - - if (i->index >= HEXIUM_INPUTS) - return -EINVAL; - - memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - *input = hexium->cur_input; - - DEB_D("VIDIOC_G_INPUT: %d\n", *input); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - DEB_EE("VIDIOC_S_INPUT %d\n", input); - - if (input >= HEXIUM_INPUTS) - return -EINVAL; - - hexium->cur_input = input; - hexium_set_input(hexium, input); - return 0; -} - -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct hexium *hexium; - int ret; - - DEB_EE("\n"); - - hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); - if (!hexium) - return -ENOMEM; - - dev->ext_priv = hexium; - - /* enable i2c-port pins */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); - - strscpy(hexium->i2c_adapter.name, "hexium gemini", - sizeof(hexium->i2c_adapter.name)); - saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { - DEB_S("cannot register i2c-device. skipping.\n"); - kfree(hexium); - return -EFAULT; - } - - /* set HWControl GPIO number 2 */ - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - - saa7146_write(dev, DD1_INIT, 0x07000700); - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - /* the rest */ - hexium->cur_input = 0; - hexium_init_done(dev); - - hexium_set_standard(hexium, hexium_pal); - hexium->cur_std = V4L2_STD_PAL; - - hexium_set_input(hexium, 0); - hexium->cur_input = 0; - - ret = saa7146_vv_init(dev, &vv_data); - if (ret) { - i2c_del_adapter(&hexium->i2c_adapter); - kfree(hexium); - return ret; - } - - vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data.vid_ops.vidioc_g_input = vidioc_g_input; - vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO); - if (ret < 0) { - pr_err("cannot register capture v4l2 device. skipping.\n"); - saa7146_vv_release(dev); - i2c_del_adapter(&hexium->i2c_adapter); - kfree(hexium); - return ret; - } - - pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num); - hexium_num++; - - return 0; -} - -static int hexium_detach(struct saa7146_dev *dev) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - DEB_EE("dev:%p\n", dev); - - saa7146_unregister_device(&hexium->video_dev, dev); - saa7146_vv_release(dev); - - hexium_num--; - - i2c_del_adapter(&hexium->i2c_adapter); - kfree(hexium); - return 0; -} - -static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - if (V4L2_STD_PAL == std->id) { - hexium_set_standard(hexium, hexium_pal); - hexium->cur_std = V4L2_STD_PAL; - return 0; - } else if (V4L2_STD_NTSC == std->id) { - hexium_set_standard(hexium, hexium_ntsc); - hexium->cur_std = V4L2_STD_NTSC; - return 0; - } else if (V4L2_STD_SECAM == std->id) { - hexium_set_standard(hexium, hexium_secam); - hexium->cur_std = V4L2_STD_SECAM; - return 0; - } - - return -1; -} - -static struct saa7146_extension hexium_extension; - -static struct saa7146_pci_extension_data hexium_gemini_4bnc = { - .ext_priv = "Hexium Gemini (4 BNC)", - .ext = &hexium_extension, -}; - -static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { - .ext_priv = "Hexium Gemini Dual (4 BNC)", - .ext = &hexium_extension, -}; - -static const struct pci_device_id pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2401, - .driver_data = (unsigned long) &hexium_gemini_4bnc, - }, - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2402, - .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, - }, - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_ext_vv vv_data = { - .inputs = HEXIUM_INPUTS, - .capabilities = 0, - .stds = &hexium_standards[0], - .num_stds = ARRAY_SIZE(hexium_standards), - .std_callback = &std_callback, -}; - -static struct saa7146_extension hexium_extension = { - .name = "hexium gemini", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = &pci_tbl[0], - .module = THIS_MODULE, - - .attach = hexium_attach, - .detach = hexium_detach, - - .irq_mask = 0, - .irq_func = NULL, -}; - -static int __init hexium_init_module(void) -{ - if (0 != saa7146_register_extension(&hexium_extension)) { - DEB_S("failed to register extension\n"); - return -ENODEV; - } - - return 0; -} - -static void __exit hexium_cleanup_module(void) -{ - saa7146_unregister_extension(&hexium_extension); -} - -module_init(hexium_init_module); -module_exit(hexium_cleanup_module); - -MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards"); -MODULE_AUTHOR("Michael Hunold "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c deleted file mode 100644 index 2eb4bee16b71..000000000000 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards - - Visit http://www.mihu.de/linux/saa7146/ and follow the link - to "hexium" for further details about this card. - - Copyright (C) 2003 Michael Hunold - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DEBUG_VARIABLE debug - -#include -#include -#include - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "debug verbosity"); - -/* global variables */ -static int hexium_num; - -#define HEXIUM_HV_PCI6_ORION 1 -#define HEXIUM_ORION_1SVHS_3BNC 2 -#define HEXIUM_ORION_4BNC 3 - -#define HEXIUM_INPUTS 9 -static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, -}; - -#define HEXIUM_AUDIOS 0 - -struct hexium_data -{ - s8 adr; - u8 byte; -}; - -struct hexium -{ - int type; - struct video_device video_dev; - struct i2c_adapter i2c_adapter; - - int cur_input; /* current input */ -}; - -/* Philips SAA7110 decoder default registers */ -static u8 hexium_saa7110[53]={ -/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00, -/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90, -/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA, -/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00, -/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F, -/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03, -/*30*/ 0x44,0x75,0x01,0x8C,0x03 -}; - -static struct { - struct hexium_data data[8]; -} hexium_input_select[] = { -{ - { /* cvbs 1 */ - { 0x06, 0x00 }, - { 0x20, 0xD9 }, - { 0x21, 0x17 }, // 0x16, - { 0x22, 0x40 }, - { 0x2C, 0x03 }, - { 0x30, 0x44 }, - { 0x31, 0x75 }, // ?? - { 0x21, 0x16 }, // 0x03, - } -}, { - { /* cvbs 2 */ - { 0x06, 0x00 }, - { 0x20, 0x78 }, - { 0x21, 0x07 }, // 0x03, - { 0x22, 0xD2 }, - { 0x2C, 0x83 }, - { 0x30, 0x60 }, - { 0x31, 0xB5 }, // ? - { 0x21, 0x03 }, - } -}, { - { /* cvbs 3 */ - { 0x06, 0x00 }, - { 0x20, 0xBA }, - { 0x21, 0x07 }, // 0x05, - { 0x22, 0x91 }, - { 0x2C, 0x03 }, - { 0x30, 0x60 }, - { 0x31, 0xB5 }, // ?? - { 0x21, 0x05 }, // 0x03, - } -}, { - { /* cvbs 4 */ - { 0x06, 0x00 }, - { 0x20, 0xD8 }, - { 0x21, 0x17 }, // 0x16, - { 0x22, 0x40 }, - { 0x2C, 0x03 }, - { 0x30, 0x44 }, - { 0x31, 0x75 }, // ?? - { 0x21, 0x16 }, // 0x03, - } -}, { - { /* cvbs 5 */ - { 0x06, 0x00 }, - { 0x20, 0xB8 }, - { 0x21, 0x07 }, // 0x05, - { 0x22, 0x91 }, - { 0x2C, 0x03 }, - { 0x30, 0x60 }, - { 0x31, 0xB5 }, // ?? - { 0x21, 0x05 }, // 0x03, - } -}, { - { /* cvbs 6 */ - { 0x06, 0x00 }, - { 0x20, 0x7C }, - { 0x21, 0x07 }, // 0x03 - { 0x22, 0xD2 }, - { 0x2C, 0x83 }, - { 0x30, 0x60 }, - { 0x31, 0xB5 }, // ?? - { 0x21, 0x03 }, - } -}, { - { /* y/c 1 */ - { 0x06, 0x80 }, - { 0x20, 0x59 }, - { 0x21, 0x17 }, - { 0x22, 0x42 }, - { 0x2C, 0xA3 }, - { 0x30, 0x44 }, - { 0x31, 0x75 }, - { 0x21, 0x12 }, - } -}, { - { /* y/c 2 */ - { 0x06, 0x80 }, - { 0x20, 0x9A }, - { 0x21, 0x17 }, - { 0x22, 0xB1 }, - { 0x2C, 0x13 }, - { 0x30, 0x60 }, - { 0x31, 0xB5 }, - { 0x21, 0x14 }, - } -}, { - { /* y/c 3 */ - { 0x06, 0x80 }, - { 0x20, 0x3C }, - { 0x21, 0x27 }, - { 0x22, 0xC1 }, - { 0x2C, 0x23 }, - { 0x30, 0x44 }, - { 0x31, 0x75 }, - { 0x21, 0x21 }, - } -} -}; - -static struct saa7146_standard hexium_standards[] = { - { - .name = "PAL", .id = V4L2_STD_PAL, - .v_offset = 16, .v_field = 288, - .h_offset = 1, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 16, .v_field = 240, - .h_offset = 1, .h_pixels = 640, - .v_max_out = 480, .h_max_out = 640, - }, { - .name = "SECAM", .id = V4L2_STD_SECAM, - .v_offset = 16, .v_field = 288, - .h_offset = 1, .h_pixels = 720, - .v_max_out = 576, .h_max_out = 768, - } -}; - -/* this is only called for old HV-PCI6/Orion cards - without eeprom */ -static int hexium_probe(struct saa7146_dev *dev) -{ - struct hexium *hexium = NULL; - union i2c_smbus_data data; - int err = 0; - - DEB_EE("\n"); - - /* there are no hexium orion cards with revision 0 saa7146s */ - if (0 == dev->revision) { - return -EFAULT; - } - - hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); - if (!hexium) - return -ENOMEM; - - /* enable i2c-port pins */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); - - saa7146_write(dev, DD1_INIT, 0x01000100); - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - strscpy(hexium->i2c_adapter.name, "hexium orion", - sizeof(hexium->i2c_adapter.name)); - saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { - DEB_S("cannot register i2c-device. skipping.\n"); - kfree(hexium); - return -EFAULT; - } - - /* set SAA7110 control GPIO 0 */ - saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI); - /* set HWControl GPIO number 2 */ - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - - mdelay(10); - - /* detect newer Hexium Orion cards by subsystem ids */ - if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) { - pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n"); - /* we store the pointer in our private data field */ - dev->ext_priv = hexium; - hexium->type = HEXIUM_ORION_1SVHS_3BNC; - return 0; - } - - if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) { - pr_info("device is a Hexium Orion w/ 4 BNC inputs\n"); - /* we store the pointer in our private data field */ - dev->ext_priv = hexium; - hexium->type = HEXIUM_ORION_4BNC; - return 0; - } - - /* check if this is an old hexium Orion card by looking at - a saa7110 at address 0x4e */ - err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, - 0x00, I2C_SMBUS_BYTE_DATA, &data); - if (err == 0) { - pr_info("device is a Hexium HV-PCI6/Orion (old)\n"); - /* we store the pointer in our private data field */ - dev->ext_priv = hexium; - hexium->type = HEXIUM_HV_PCI6_ORION; - return 0; - } - - i2c_del_adapter(&hexium->i2c_adapter); - kfree(hexium); - return -EFAULT; -} - -/* bring hardware to a sane state. this has to be done, just in case someone - wants to capture from this device before it has been properly initialized. - the capture engine would badly fail, because no valid signal arrives on the - saa7146, thus leading to timeouts and stuff. */ -static int hexium_init_done(struct saa7146_dev *dev) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - union i2c_smbus_data data; - int i = 0; - - DEB_D("hexium_init_done called\n"); - - /* initialize the helper ics to useful values */ - for (i = 0; i < sizeof(hexium_saa7110); i++) { - data.byte = hexium_saa7110[i]; - if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { - pr_err("failed for address 0x%02x\n", i); - } - } - - return 0; -} - -static int hexium_set_input(struct hexium *hexium, int input) -{ - union i2c_smbus_data data; - int i = 0; - - DEB_D("\n"); - - for (i = 0; i < 8; i++) { - int adr = hexium_input_select[input].data[i].adr; - data.byte = hexium_input_select[input].data[i].byte; - if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) { - return -1; - } - pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte); - } - - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); - - if (i->index >= HEXIUM_INPUTS) - return -EINVAL; - - memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - *input = hexium->cur_input; - - DEB_D("VIDIOC_G_INPUT: %d\n", *input); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - if (input >= HEXIUM_INPUTS) - return -EINVAL; - - hexium->cur_input = input; - hexium_set_input(hexium, input); - - return 0; -} - -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int ret; - - DEB_EE("\n"); - - ret = saa7146_vv_init(dev, &vv_data); - if (ret) { - pr_err("Error in saa7146_vv_init()\n"); - return ret; - } - - vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data.vid_ops.vidioc_g_input = vidioc_g_input; - vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) { - pr_err("cannot register capture v4l2 device. skipping.\n"); - return -1; - } - - pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num); - hexium_num++; - - /* the rest */ - hexium->cur_input = 0; - hexium_init_done(dev); - - return 0; -} - -static int hexium_detach(struct saa7146_dev *dev) -{ - struct hexium *hexium = (struct hexium *) dev->ext_priv; - - DEB_EE("dev:%p\n", dev); - - saa7146_unregister_device(&hexium->video_dev, dev); - saa7146_vv_release(dev); - - hexium_num--; - - i2c_del_adapter(&hexium->i2c_adapter); - kfree(hexium); - return 0; -} - -static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) -{ - return 0; -} - -static struct saa7146_extension extension; - -static struct saa7146_pci_extension_data hexium_hv_pci6 = { - .ext_priv = "Hexium HV-PCI6 / Orion", - .ext = &extension, -}; - -static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = { - .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)", - .ext = &extension, -}; - -static struct saa7146_pci_extension_data hexium_orion_4bnc = { - .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)", - .ext = &extension, -}; - -static const struct pci_device_id pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, - .driver_data = (unsigned long) &hexium_hv_pci6, - }, - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x0101, - .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc, - }, - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2101, - .driver_data = (unsigned long) &hexium_orion_4bnc, - }, - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_ext_vv vv_data = { - .inputs = HEXIUM_INPUTS, - .capabilities = 0, - .stds = &hexium_standards[0], - .num_stds = ARRAY_SIZE(hexium_standards), - .std_callback = &std_callback, -}; - -static struct saa7146_extension extension = { - .name = "hexium HV-PCI6 Orion", - .flags = 0, // SAA7146_USE_I2C_IRQ, - - .pci_tbl = &pci_tbl[0], - .module = THIS_MODULE, - - .probe = hexium_probe, - .attach = hexium_attach, - .detach = hexium_detach, - - .irq_mask = 0, - .irq_func = NULL, -}; - -static int __init hexium_init_module(void) -{ - if (0 != saa7146_register_extension(&extension)) { - DEB_S("failed to register extension\n"); - return -ENODEV; - } - - return 0; -} - -static void __exit hexium_cleanup_module(void) -{ - saa7146_unregister_extension(&extension); -} - -module_init(hexium_init_module); -module_exit(hexium_cleanup_module); - -MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards"); -MODULE_AUTHOR("Michael Hunold "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c deleted file mode 100644 index 7ded8f5b05cb..000000000000 --- a/drivers/media/pci/saa7146/mxb.c +++ /dev/null @@ -1,873 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - mxb - v4l2 driver for the Multimedia eXtension Board - - Copyright (C) 1998-2006 Michael Hunold - - Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html - for further details about this card. - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DEBUG_VARIABLE debug - -#include -#include -#include -#include -#include -#include - -#include "tea6415c.h" -#include "tea6420.h" - -#define MXB_AUDIOS 6 - -#define I2C_SAA7111A 0x24 -#define I2C_TDA9840 0x42 -#define I2C_TEA6415C 0x43 -#define I2C_TEA6420_1 0x4c -#define I2C_TEA6420_2 0x4d -#define I2C_TUNER 0x60 - -#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) - -/* global variable */ -static int mxb_num; - -/* initial frequence the tuner will be tuned to. - in verden (lower saxony, germany) 4148 is a - channel called "phoenix" */ -static int freq = 4148; -module_param(freq, int, 0644); -MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); - -#define MXB_INPUTS 4 -enum { TUNER, AUX1, AUX3, AUX3_YC }; - -static struct v4l2_input mxb_inputs[MXB_INPUTS] = { - { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, - V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, - { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, - V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, - V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, - V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, -}; - -/* this array holds the information, which port of the saa7146 each - input actually uses. the mxb uses port 0 for every input */ -static struct { - int hps_source; - int hps_sync; -} input_port_selection[MXB_INPUTS] = { - { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, - { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, - { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, - { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, -}; - -/* this array holds the information of the audio source (mxb_audios), - which has to be switched corresponding to the video source (mxb_channels) */ -static int video_audio_connect[MXB_INPUTS] = - { 0, 1, 3, 3 }; - -struct mxb_routing { - u32 input; - u32 output; -}; - -/* these are the available audio sources, which can switched - to the line- and cd-output individually */ -static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { - { - .index = 0, - .name = "Tuner", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 1, - .name = "AUX1", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 2, - .name = "AUX2", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 3, - .name = "AUX3", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 4, - .name = "Radio (X9)", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 5, - .name = "CD-ROM (X10)", - .capability = V4L2_AUDCAP_STEREO, - } -}; - -/* These are the necessary input-output-pins for bringing one audio source - (see above) to the CD-output. Note that gain is set to 0 in this table. */ -static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { - { { 1, 1 }, { 1, 1 } }, /* Tuner */ - { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ - { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ - { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ - { { 1, 1 }, { 3, 1 } }, /* Radio */ - { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ - { { 6, 1 }, { 6, 1 } } /* Mute */ -}; - -/* These are the necessary input-output-pins for bringing one audio source - (see above) to the line-output. Note that gain is set to 0 in this table. */ -static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { - { { 2, 3 }, { 1, 2 } }, - { { 5, 3 }, { 6, 2 } }, - { { 4, 3 }, { 6, 2 } }, - { { 3, 3 }, { 6, 2 } }, - { { 2, 3 }, { 3, 2 } }, - { { 2, 3 }, { 2, 2 } }, - { { 6, 3 }, { 6, 2 } } /* Mute */ -}; - -struct mxb -{ - struct video_device video_dev; - struct video_device vbi_dev; - - struct i2c_adapter i2c_adapter; - - struct v4l2_subdev *saa7111a; - struct v4l2_subdev *tda9840; - struct v4l2_subdev *tea6415c; - struct v4l2_subdev *tuner; - struct v4l2_subdev *tea6420_1; - struct v4l2_subdev *tea6420_2; - - int cur_mode; /* current audio mode (mono, stereo, ...) */ - int cur_input; /* current input */ - int cur_audinput; /* current audio input */ - int cur_mute; /* current mute status */ - struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ -}; - -#define saa7111a_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->saa7111a, o, f, ##args) -#define tda9840_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->tda9840, o, f, ##args) -#define tea6415c_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->tea6415c, o, f, ##args) -#define tuner_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->tuner, o, f, ##args) -#define call_all(dev, o, f, args...) \ - v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) - -static void mxb_update_audmode(struct mxb *mxb) -{ - struct v4l2_tuner t = { - .audmode = mxb->cur_mode, - }; - - tda9840_call(mxb, tuner, s_tuner, &t); -} - -static inline void tea6420_route(struct mxb *mxb, int idx) -{ - v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, - TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); - v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, - TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); - v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, - TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); - v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, - TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); -} - -static struct saa7146_extension extension; - -static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct saa7146_dev *dev = container_of(ctrl->handler, - struct saa7146_dev, ctrl_handler); - struct mxb *mxb = dev->ext_priv; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - mxb->cur_mute = ctrl->val; - /* switch the audio-source */ - tea6420_route(mxb, ctrl->val ? 6 : - video_audio_connect[mxb->cur_input]); - break; - default: - return -EINVAL; - } - return 0; -} - -static const struct v4l2_ctrl_ops mxb_ctrl_ops = { - .s_ctrl = mxb_s_ctrl, -}; - -static int mxb_probe(struct saa7146_dev *dev) -{ - struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; - struct mxb *mxb = NULL; - - v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); - if (hdl->error) - return hdl->error; - mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); - if (mxb == NULL) { - DEB_D("not enough kernel memory\n"); - return -ENOMEM; - } - - - snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); - - saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { - DEB_S("cannot register i2c-device. skipping.\n"); - kfree(mxb); - return -EFAULT; - } - - mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "saa7111", I2C_SAA7111A, NULL); - mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6420", I2C_TEA6420_1, NULL); - mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6420", I2C_TEA6420_2, NULL); - mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tea6415c", I2C_TEA6415C, NULL); - mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tda9840", I2C_TDA9840, NULL); - mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, - "tuner", I2C_TUNER, NULL); - - /* check if all devices are present */ - if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || - !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { - pr_err("did not find all i2c devices. aborting\n"); - i2c_del_adapter(&mxb->i2c_adapter); - kfree(mxb); - return -ENODEV; - } - - /* all devices are present, probe was successful */ - - /* we store the pointer in our private data field */ - dev->ext_priv = mxb; - - v4l2_ctrl_handler_setup(hdl); - - return 0; -} - -/* some init data for the saa7740, the so-called 'sound arena module'. - there are no specs available, so we simply use some init values */ -static struct { - int length; - char data[9]; -} mxb_saa7740_init[] = { - { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, - { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, - { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, - { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, - { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, - { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, - { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, - { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, - { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, - { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, - { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, - { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, - { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, - { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, - { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, - { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, - { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, - { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, - { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, - { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, - { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, - { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, - { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, - { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, - { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, - { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, - { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, - { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, - { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, - { 3, { 0x48, 0x00, 0x01 } }, - { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, - { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, - { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, - { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, - { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, - { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, - { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, - { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, - { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, - { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, - { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, - { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, - { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, - { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, - { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, - { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, - { 3, { 0x80, 0xb3, 0x0a } }, - {-1, { 0 } } -}; - -/* bring hardware to a sane state. this has to be done, just in case someone - wants to capture from this device before it has been properly initialized. - the capture engine would badly fail, because no valid signal arrives on the - saa7146, thus leading to timeouts and stuff. */ -static int mxb_init_done(struct saa7146_dev* dev) -{ - struct mxb* mxb = (struct mxb*)dev->ext_priv; - struct i2c_msg msg; - struct tuner_setup tun_setup; - v4l2_std_id std = V4L2_STD_PAL_BG; - - int i, err = 0; - - /* mute audio on tea6420s */ - tea6420_route(mxb, 6); - - /* select video mode in saa7111a */ - saa7111a_call(mxb, video, s_std, std); - - /* select tuner-output on saa7111a */ - saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, - SAA7111_FMT_CCIR, 0); - - /* select a tuner type */ - tun_setup.mode_mask = T_ANALOG_TV; - tun_setup.addr = ADDR_UNSET; - tun_setup.type = TUNER_PHILIPS_PAL; - tuner_call(mxb, tuner, s_type_addr, &tun_setup); - /* tune in some frequency on tuner */ - mxb->cur_freq.tuner = 0; - mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; - mxb->cur_freq.frequency = freq; - tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); - - /* set a default video standard */ - /* These two gpio calls set the GPIO pins that control the tda9820 */ - saa7146_write(dev, GPIO_CTRL, 0x00404050); - saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, video, s_std, std); - tuner_call(mxb, video, s_std, std); - - /* switch to tuner-channel on tea6415c */ - tea6415c_call(mxb, video, s_routing, 3, 17, 0); - - /* select tuner-output on multicable on tea6415c */ - tea6415c_call(mxb, video, s_routing, 3, 13, 0); - - /* the rest for mxb */ - mxb->cur_input = 0; - mxb->cur_audinput = video_audio_connect[mxb->cur_input]; - mxb->cur_mute = 1; - - mxb->cur_mode = V4L2_TUNER_MODE_STEREO; - mxb_update_audmode(mxb); - - /* check if the saa7740 (aka 'sound arena module') is present - on the mxb. if so, we must initialize it. due to lack of - information about the saa7740, the values were reverse - engineered. */ - msg.addr = 0x1b; - msg.flags = 0; - msg.len = mxb_saa7740_init[0].length; - msg.buf = &mxb_saa7740_init[0].data[0]; - - err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); - if (err == 1) { - /* the sound arena module is a pos, that's probably the reason - philips refuses to hand out a datasheet for the saa7740... - it seems to screw up the i2c bus, so we disable fast irq - based i2c transactions here and rely on the slow and safe - polling method ... */ - extension.flags &= ~SAA7146_USE_I2C_IRQ; - for (i = 1; ; i++) { - if (-1 == mxb_saa7740_init[i].length) - break; - - msg.len = mxb_saa7740_init[i].length; - msg.buf = &mxb_saa7740_init[i].data[0]; - err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); - if (err != 1) { - DEB_D("failed to initialize 'sound arena module'\n"); - goto err; - } - } - pr_info("'sound arena module' detected\n"); - } -err: - /* the rest for saa7146: you should definitely set some basic values - for the input-port handling of the saa7146. */ - - /* ext->saa has been filled by the core driver */ - - /* some stuff is done via variables */ - saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, - input_port_selection[mxb->cur_input].hps_sync); - - /* some stuff is done via direct write to the registers */ - - /* this is ugly, but because of the fact that this is completely - hardware dependend, it should be done directly... */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, DD1_INIT, 0x02000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - return 0; -} - -/* interrupt-handler. this gets called when irq_mask is != 0. - it must clear the interrupt-bits in irq_mask it has handled */ -/* -void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) -{ - struct mxb* mxb = (struct mxb*)dev->ext_priv; -} -*/ - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); - if (i->index >= MXB_INPUTS) - return -EINVAL; - memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - *i = mxb->cur_input; - - DEB_EE("VIDIOC_G_INPUT %d\n", *i); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int err = 0; - int i = 0; - - DEB_EE("VIDIOC_S_INPUT %d\n", input); - - if (input >= MXB_INPUTS) - return -EINVAL; - - mxb->cur_input = input; - - saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, - input_port_selection[input].hps_sync); - - /* prepare switching of tea6415c and saa7111a; - have a look at the 'background'-file for further information */ - switch (input) { - case TUNER: - i = SAA7115_COMPOSITE0; - - err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); - - /* connect tuner-output always to multicable */ - if (!err) - err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); - break; - case AUX3_YC: - /* nothing to be done here. aux3_yc is - directly connected to the saa711a */ - i = SAA7115_SVIDEO1; - break; - case AUX3: - /* nothing to be done here. aux3 is - directly connected to the saa711a */ - i = SAA7115_COMPOSITE1; - break; - case AUX1: - i = SAA7115_COMPOSITE0; - err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); - break; - } - - if (err) - return err; - - /* switch video in saa7111a */ - if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) - pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); - - mxb->cur_audinput = video_audio_connect[input]; - /* switch the audio-source only if necessary */ - if (0 == mxb->cur_mute) - tea6420_route(mxb, mxb->cur_audinput); - if (mxb->cur_audinput == 0) - mxb_update_audmode(mxb); - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - if (t->index) { - DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", - t->index); - return -EINVAL; - } - - DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); - - memset(t, 0, sizeof(*t)); - strscpy(t->name, "TV Tuner", sizeof(t->name)); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->audmode = mxb->cur_mode; - return call_all(dev, tuner, g_tuner, t); -} - -static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - if (t->index) { - DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", - t->index); - return -EINVAL; - } - - mxb->cur_mode = t->audmode; - return call_all(dev, tuner, s_tuner, t); -} - -static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - - return call_all(dev, video, querystd, norm); -} - -static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - if (f->tuner) - return -EINVAL; - *f = mxb->cur_freq; - - DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - struct saa7146_vv *vv = dev->vv_data; - - if (f->tuner) - return -EINVAL; - - if (V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - - DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); - - /* tune in desired frequency */ - tuner_call(mxb, tuner, s_frequency, f); - /* let the tuner subdev clamp the frequency to the tuner range */ - mxb->cur_freq = *f; - tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); - if (mxb->cur_audinput == 0) - mxb_update_audmode(mxb); - - if (mxb->cur_input) - return 0; - - /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ - spin_lock(&dev->slock); - vv->vbi_fieldcount = 0; - spin_unlock(&dev->slock); - - return 0; -} - -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - if (a->index >= MXB_AUDIOS) - return -EINVAL; - *a = mxb_audios[a->index]; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - DEB_EE("VIDIOC_G_AUDIO\n"); - *a = mxb_audios[mxb->cur_audinput]; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - if (a->index >= 32 || - !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) - return -EINVAL; - - if (mxb->cur_audinput != a->index) { - mxb->cur_audinput = a->index; - tea6420_route(mxb, a->index); - if (mxb->cur_audinput == 0) - mxb_update_audmode(mxb); - } - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - - if (reg->reg > pci_resource_len(dev->pci, 0) - 4) - return -EINVAL; - reg->val = saa7146_read(dev, reg->reg); - reg->size = 4; - return 0; -} - -static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - - if (reg->reg > pci_resource_len(dev->pci, 0) - 4) - return -EINVAL; - saa7146_write(dev, reg->reg, reg->val); - return 0; -} -#endif - -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct mxb *mxb; - int ret; - - DEB_EE("dev:%p\n", dev); - - ret = saa7146_vv_init(dev, &vv_data); - if (ret) { - ERR("Error in saa7146_vv_init()"); - return ret; - } - - if (mxb_probe(dev)) { - saa7146_vv_release(dev); - return -1; - } - mxb = (struct mxb *)dev->ext_priv; - - vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data.vid_ops.vidioc_g_input = vidioc_g_input; - vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - vv_data.vid_ops.vidioc_querystd = vidioc_querystd; - vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; - vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; - vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; -#ifdef CONFIG_VIDEO_ADV_DEBUG - vv_data.vid_ops.vidioc_g_register = vidioc_g_register; - vv_data.vid_ops.vidioc_s_register = vidioc_s_register; -#endif - if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { - ERR("cannot register capture v4l2 device. skipping.\n"); - saa7146_vv_release(dev); - return -1; - } - - /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if (MXB_BOARD_CAN_DO_VBI(dev)) { - if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { - ERR("cannot register vbi v4l2 device. skipping.\n"); - } - } - - pr_info("found Multimedia eXtension Board #%d\n", mxb_num); - - mxb_num++; - mxb_init_done(dev); - return 0; -} - -static int mxb_detach(struct saa7146_dev *dev) -{ - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - DEB_EE("dev:%p\n", dev); - - /* mute audio on tea6420s */ - tea6420_route(mxb, 6); - - saa7146_unregister_device(&mxb->video_dev,dev); - if (MXB_BOARD_CAN_DO_VBI(dev)) - saa7146_unregister_device(&mxb->vbi_dev, dev); - saa7146_vv_release(dev); - - mxb_num--; - - i2c_del_adapter(&mxb->i2c_adapter); - kfree(mxb); - - return 0; -} - -static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) -{ - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - if (V4L2_STD_PAL_I == standard->id) { - v4l2_std_id std = V4L2_STD_PAL_I; - - DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); - /* These two gpio calls set the GPIO pins that control the tda9820 */ - saa7146_write(dev, GPIO_CTRL, 0x00404050); - saa7111a_call(mxb, core, s_gpio, 0); - saa7111a_call(mxb, video, s_std, std); - if (mxb->cur_input == 0) - tuner_call(mxb, video, s_std, std); - } else { - v4l2_std_id std = V4L2_STD_PAL_BG; - - if (mxb->cur_input) - std = standard->id; - DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); - /* These two gpio calls set the GPIO pins that control the tda9820 */ - saa7146_write(dev, GPIO_CTRL, 0x00404050); - saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, video, s_std, std); - if (mxb->cur_input == 0) - tuner_call(mxb, video, s_std, std); - } - return 0; -} - -static struct saa7146_standard standard[] = { - { - .name = "PAL-BG", .id = V4L2_STD_PAL_BG, - .v_offset = 0x17, .v_field = 288, - .h_offset = 0x14, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "PAL-I", .id = V4L2_STD_PAL_I, - .v_offset = 0x17, .v_field = 288, - .h_offset = 0x14, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x16, .v_field = 240, - .h_offset = 0x06, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - }, { - .name = "SECAM", .id = V4L2_STD_SECAM, - .v_offset = 0x14, .v_field = 288, - .h_offset = 0x14, .h_pixels = 720, - .v_max_out = 576, .h_max_out = 768, - } -}; - -static struct saa7146_pci_extension_data mxb = { - .ext_priv = "Multimedia eXtension Board", - .ext = &extension, -}; - -static const struct pci_device_id pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, - .driver_data = (unsigned long)&mxb, - }, { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_ext_vv vv_data = { - .inputs = MXB_INPUTS, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), - .std_callback = &std_callback, -}; - -static struct saa7146_extension extension = { - .name = "Multimedia eXtension Board", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = &pci_tbl[0], - .module = THIS_MODULE, - - .attach = mxb_attach, - .detach = mxb_detach, - - .irq_mask = 0, - .irq_func = NULL, -}; - -static int __init mxb_init_module(void) -{ - if (saa7146_register_extension(&extension)) { - DEB_S("failed to register extension\n"); - return -ENODEV; - } - - return 0; -} - -static void __exit mxb_cleanup_module(void) -{ - saa7146_unregister_extension(&extension); -} - -module_init(mxb_init_module); -module_exit(mxb_cleanup_module); - -MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); -MODULE_AUTHOR("Michael Hunold "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig deleted file mode 100644 index 65a6832a6b96..000000000000 --- a/drivers/media/pci/ttpci/Kconfig +++ /dev/null @@ -1,86 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config DVB_BUDGET_CORE - tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)" - depends on DVB_CORE && PCI && I2C - select VIDEO_SAA7146 - select TTPCI_EEPROM - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder. - -config DVB_BUDGET - tristate "Budget cards" - depends on DVB_BUDGET_CORE && I2C - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT - select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT - select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT - select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT - select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT - help - Support for simple SAA7146 based DVB cards (so called Budget- - or Nova-PCI cards) without onboard MPEG2 decoder, and without - analog inputs or an onboard Common Interface connector. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget. - -config DVB_BUDGET_CI - tristate "Budget cards with onboard CI connector" - depends on DVB_BUDGET_CORE && I2C - select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT - depends on RC_CORE - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder, but with onboard Common Interface connector. - - Note: The Common Interface is not yet supported by this driver - due to lack of information from the vendor. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-ci. - -config DVB_BUDGET_AV - tristate "Budget cards with analog video inputs" - depends on DVB_BUDGET_CORE && I2C - select VIDEO_SAA7146_VV - depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT - help - Support for simple SAA7146 based DVB cards - (so called Budget- or Nova-PCI cards) without onboard - MPEG2 decoder, but with one or more analog video inputs. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-av. diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile deleted file mode 100644 index b0708f6e40cc..000000000000 --- a/drivers/media/pci/ttpci/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the kernel SAA7146 FULL TS DVB device driver -# - -obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o -obj-$(CONFIG_DVB_BUDGET) += budget.o -obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o -obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o - -ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ -ccflags-y += -I $(srctree)/drivers/media/tuners -ccflags-y += -I $(srctree)/drivers/media/common diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c deleted file mode 100644 index 3cb83005cf09..000000000000 --- a/drivers/media/pci/ttpci/budget-av.c +++ /dev/null @@ -1,1622 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget-av.c: driver for the SAA7146 based Budget DVB cards - * with analog video in - * - * Compiled from various sources by Michael Hunold - * - * CI interface support (c) 2004 Olivier Gournet & - * Andrew de Quincey - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * the project's page is at https://linuxtv.org - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "budget.h" -#include "stv0299.h" -#include "stb0899_drv.h" -#include "stb0899_reg.h" -#include "stb0899_cfg.h" -#include "tda8261.h" -#include "tda8261_cfg.h" -#include "tda1002x.h" -#include "tda1004x.h" -#include "tua6100.h" -#include "dvb-pll.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DEBICICAM 0x02420000 - -#define SLOTSTATUS_NONE 1 -#define SLOTSTATUS_PRESENT 2 -#define SLOTSTATUS_RESET 4 -#define SLOTSTATUS_READY 8 -#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct budget_av { - struct budget budget; - struct video_device vd; - int cur_input; - int has_saa7113; - struct tasklet_struct ciintf_irq_tasklet; - int slot_status; - struct dvb_ca_en50221 ca; - u8 reinitialise_demod:1; -}; - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); - - -/* GPIO Connections: - * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! - * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory - * 2 - CI Card Enable (Active Low) - * 3 - CI Card Detect - */ - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - -static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) -{ - u8 mm1[] = { 0x00 }; - u8 mm2[] = { 0x00 }; - struct i2c_msg msgs[2]; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = id / 2; - mm1[0] = reg; - msgs[0].len = 1; - msgs[1].len = 1; - msgs[0].buf = mm1; - msgs[1].buf = mm2; - - i2c_transfer(i2c, msgs, 2); - - return mm2[0]; -} - -static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) -{ - u8 mm1[] = { reg }; - struct i2c_msg msgs[2] = { - {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, - {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} - }; - - if (i2c_transfer(i2c, msgs, 2) != 2) - return -EIO; - - return 0; -} - -static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) -{ - u8 msg[2] = { reg, val }; - struct i2c_msg msgs; - - msgs.flags = 0; - msgs.addr = id / 2; - msgs.len = 2; - msgs.buf = msg; - return i2c_transfer(i2c, &msgs, 1); -} - -static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); - udelay(1); - - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 1\n"); - } - return result; -} - -static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); - udelay(1); - - result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 2\n"); - } - return result; -} - -static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - udelay(1); - - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 3\n"); - return -ETIMEDOUT; - } - return result; -} - -static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - int result; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - udelay(1); - - result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); - if (result == -ETIMEDOUT) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 5\n"); - } - return result; -} - -static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_reset\n"); - budget_av->slot_status = SLOTSTATUS_RESET; - - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ - msleep(2); - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ - msleep(20); /* 20 ms Vcc settling time */ - - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - msleep(20); - - /* reinitialise the frontend if necessary */ - if (budget_av->reinitialise_demod) - dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); - - return 0; -} - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_shutdown\n"); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - budget_av->slot_status = SLOTSTATUS_NONE; - - return 0; -} - -static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - - if (slot != 0) - return -EINVAL; - - dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - - return 0; -} - -static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct budget_av *budget_av = (struct budget_av *) ca->data; - struct saa7146_dev *saa = budget_av->budget.dev; - int result; - - if (slot != 0) - return -EINVAL; - - /* test the card detect line - needs to be done carefully - * since it never goes high for some CAMs on this interface (e.g. topuptv) */ - if (budget_av->slot_status == SLOTSTATUS_NONE) { - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - udelay(1); - if (saa7146_read(saa, PSR) & MASK_06) { - if (budget_av->slot_status == SLOTSTATUS_NONE) { - budget_av->slot_status = SLOTSTATUS_PRESENT; - pr_info("cam inserted A\n"); - } - } - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - } - - /* We also try and read from IO memory to work round the above detection bug. If - * there is no CAM, we will get a timeout. Only done if there is no cam - * present, since this test actually breaks some cams :( - * - * if the CI interface is not open, we also do the above test since we - * don't care if the cam has problems - we'll be resetting it on open() anyway */ - if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); - if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { - budget_av->slot_status = SLOTSTATUS_PRESENT; - pr_info("cam inserted B\n"); - } else if (result < 0) { - if (budget_av->slot_status != SLOTSTATUS_NONE) { - ciintf_slot_shutdown(ca, slot); - pr_info("cam ejected 5\n"); - return 0; - } - } - } - - /* read from attribute memory in reset/ready state to know when the CAM is ready */ - if (budget_av->slot_status == SLOTSTATUS_RESET) { - result = ciintf_read_attribute_mem(ca, slot, 0); - if (result == 0x1d) { - budget_av->slot_status = SLOTSTATUS_READY; - } - } - - /* work out correct return code */ - if (budget_av->slot_status != SLOTSTATUS_NONE) { - if (budget_av->slot_status & SLOTSTATUS_READY) { - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - } - return DVB_CA_EN50221_POLL_CAM_PRESENT; - } - return 0; -} - -static int ciintf_init(struct budget_av *budget_av) -{ - struct saa7146_dev *saa = budget_av->budget.dev; - int result; - - memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - - /* Enable DEBI pins */ - saa7146_write(saa, MC1, MASK_27 | MASK_11); - - /* register CI interface */ - budget_av->ca.owner = THIS_MODULE; - budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; - budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; - budget_av->ca.read_cam_control = ciintf_read_cam_control; - budget_av->ca.write_cam_control = ciintf_write_cam_control; - budget_av->ca.slot_reset = ciintf_slot_reset; - budget_av->ca.slot_shutdown = ciintf_slot_shutdown; - budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; - budget_av->ca.poll_slot_status = ciintf_poll_slot_status; - budget_av->ca.data = budget_av; - budget_av->budget.ci_present = 1; - budget_av->slot_status = SLOTSTATUS_NONE; - - if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, - &budget_av->ca, 0, 1)) != 0) { - pr_err("ci initialisation failed\n"); - goto error; - } - - pr_info("ci interface initialised\n"); - return 0; - -error: - saa7146_write(saa, MC1, MASK_27); - return result; -} - -static void ciintf_deinit(struct budget_av *budget_av) -{ - struct saa7146_dev *saa = budget_av->budget.dev; - - saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - - /* release the CA device */ - dvb_ca_en50221_release(&budget_av->ca); - - /* disable DEBI pins */ - saa7146_write(saa, MC1, MASK_27); -} - - -static const u8 saa7113_tab[] = { - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x33, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xeb, - 0x07, 0xe0, - 0x08, 0x28, - 0x09, 0x00, - 0x0a, 0x80, - 0x0b, 0x47, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x44, - - 0x10, 0x08, - 0x11, 0x0c, - 0x12, 0x7b, - 0x13, 0x00, - 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, - - 0x57, 0xff, - 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, - 0x5b, 0x83, 0x5e, 0x00, - 0xff -}; - -static int saa7113_init(struct budget_av *budget_av) -{ - struct budget *budget = &budget_av->budget; - struct saa7146_dev *saa = budget->dev; - const u8 *data = saa7113_tab; - - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); - msleep(200); - - if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { - dprintk(1, "saa7113 not found on KNC card\n"); - return -ENODEV; - } - - dprintk(1, "saa7113 detected and initializing\n"); - - while (*data != 0xff) { - i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); - data += 2; - } - - dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); - - return 0; -} - -static int saa7113_setinput(struct budget_av *budget_av, int input) -{ - struct budget *budget = &budget_av->budget; - - if (1 != budget_av->has_saa7113) - return -ENODEV; - - if (input == 1) { - i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); - i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); - } else if (input == 0) { - i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); - i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); - } else - return -EINVAL; - - budget_av->cur_input = input; - return 0; -} - - -static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - - aclk = 0xb5; - if (srate < 2000000) - bclk = 0x86; - else if (srate < 5000000) - bclk = 0x89; - else if (srate < 15000000) - bclk = 0x8f; - else if (srate < 45000000) - bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) - m1 = 0x10; - - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - stv0299_writereg(fe, 0x0f, 0x80 | m1); - - return 0; -} - -static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 div; - u8 buf[4]; - struct budget *budget = (struct budget *) fe->dvb->priv; - struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; - - if ((c->frequency < 950000) || (c->frequency > 2150000)) - return -EINVAL; - - div = (c->frequency + (125 - 1)) / 125; /* round correctly */ - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; - buf[3] = 0x20; - - if (c->symbol_rate < 4000000) - buf[3] |= 1; - - if (c->frequency < 1250000) - buf[3] |= 0; - else if (c->frequency < 1550000) - buf[3] |= 0x40; - else if (c->frequency < 2050000) - buf[3] |= 0x80; - else if (c->frequency < 2150000) - buf[3] |= 0xC0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static u8 typhoon_cinergy1200s_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ - 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ - 0x06, 0x40, /* DAC not used, set to high impendance mode */ - 0x07, 0x00, /* DAC LSB */ - 0x08, 0x40, /* DiSEqC off */ - 0x09, 0x00, /* FIFO */ - 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ - 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ - 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ - 0x10, 0x3f, // AGC2 0x3d - 0x11, 0x84, - 0x12, 0xb9, - 0x15, 0xc9, // lock detector threshold - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 - 0x29, 0x1e, // 1/2 threshold - 0x2a, 0x14, // 2/3 threshold - 0x2b, 0x0f, // 3/4 threshold - 0x2c, 0x09, // 5/6 threshold - 0x2d, 0x05, // 7/8 threshold - 0x2e, 0x01, - 0x31, 0x1f, // test all FECs - 0x32, 0x19, // viterbi and synchro search - 0x33, 0xfc, // rs control - 0x34, 0x93, // error control - 0x0f, 0x92, - 0xff, 0xff -}; - -static const struct stv0299_config typhoon_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - - -static const struct stv0299_config cinergy_1200s_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_0, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - -static const struct stv0299_config cinergy_1200s_1894_0010_config = { - .demod_address = 0x68, - .inittab = typhoon_cinergy1200s_inittab, - .mclk = 88000000UL, - .invert = 1, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, -}; - -static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; - u8 buf[6]; - struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; - int i; - -#define CU1216_IF 36125000 -#define TUNER_MUL 62500 - - u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0xce; - buf[3] = (c->frequency < 150000000 ? 0x01 : - c->frequency < 445000000 ? 0x02 : 0x04); - buf[4] = 0xde; - buf[5] = 0x20; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - /* wait for the pll lock */ - msg.flags = I2C_M_RD; - msg.len = 1; - for (i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) - break; - msleep(10); - } - - /* switch the charge pump to the lower current */ - msg.flags = 0; - msg.len = 2; - msg.buf = &buf[2]; - buf[2] &= ~0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - - return 0; -} - -static struct tda1002x_config philips_cu1216_config = { - .demod_address = 0x0c, - .invert = 1, -}; - -static struct tda1002x_config philips_cu1216_config_altaddress = { - .demod_address = 0x0d, - .invert = 0, -}; - -static struct tda10023_config philips_cu1216_tda10023_config = { - .demod_address = 0x0c, - .invert = 1, -}; - -static int philips_tu1216_tuner_init(struct dvb_frontend *fe) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; - - // setup PLL configuration - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - msleep(1); - - return 0; -} - -static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = - sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = c->frequency + 36166000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) - cp = 3; - else if (tuner_frequency < 160000000) - cp = 5; - else if (tuner_frequency < 200000000) - cp = 6; - else if (tuner_frequency < 290000000) - cp = 3; - else if (tuner_frequency < 420000000) - cp = 5; - else if (tuner_frequency < 480000000) - cp = 6; - else if (tuner_frequency < 620000000) - cp = 3; - else if (tuner_frequency < 830000000) - cp = 5; - else if (tuner_frequency < 895000000) - cp = 7; - else - return -EINVAL; - - // determine band - if (c->frequency < 49000000) - return -EINVAL; - else if (c->frequency < 161000000) - band = 1; - else if (c->frequency < 444000000) - band = 2; - else if (c->frequency < 861000000) - band = 4; - else - return -EINVAL; - - // setup PLL filter - switch (c->bandwidth_hz) { - case 6000000: - filter = 0; - break; - - case 7000000: - filter = 0; - break; - - case 8000000: - filter = 1; - break; - - default: - return -EINVAL; - } - - // calculate divisor - // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; - - // setup tuner buffer - tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - return 0; -} - -static int philips_tu1216_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - - return request_firmware(fw, name, &budget->dev->pci->dev); -} - -static struct tda1004x_config philips_tu1216_config = { - - .demod_address = 0x8, - .invert = 1, - .invert_oclk = 1, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, -}; - -static u8 philips_sd1878_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x7d, - 0x05, 0x35, - 0x06, 0x40, - 0x07, 0x00, - 0x08, 0x43, - 0x09, 0x02, - 0x0C, 0x51, - 0x0D, 0x82, - 0x0E, 0x23, - 0x10, 0x3f, - 0x11, 0x84, - 0x12, 0xb9, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x93, - 0xff, 0xff -}; - -static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, - u32 srate, u32 ratio) -{ - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - - aclk = 0xb5; - if (srate < 2000000) - bclk = 0x86; - else if (srate < 5000000) - bclk = 0x89; - else if (srate < 15000000) - bclk = 0x8f; - else if (srate < 45000000) - bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) - m1 = 0x10; - - stv0299_writereg(fe, 0x0e, 0x23); - stv0299_writereg(fe, 0x0f, 0x94); - stv0299_writereg(fe, 0x10, 0x39); - stv0299_writereg(fe, 0x13, aclk); - stv0299_writereg(fe, 0x14, bclk); - stv0299_writereg(fe, 0x15, 0xc9); - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - stv0299_writereg(fe, 0x0f, 0x80 | m1); - - return 0; -} - -static const struct stv0299_config philips_sd1878_config = { - .demod_address = 0x68, - .inittab = philips_sd1878_inittab, - .mclk = 88000000UL, - .invert = 0, - .skip_reinit = 0, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP0, - .min_delay_ms = 100, - .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, -}; - -/* KNC1 DVB-S (STB0899) Inittab */ -static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { - - { STB0899_DEV_ID , 0x81 }, - { STB0899_DISCNTRL1 , 0x32 }, - { STB0899_DISCNTRL2 , 0x80 }, - { STB0899_DISRX_ST0 , 0x04 }, - { STB0899_DISRX_ST1 , 0x00 }, - { STB0899_DISPARITY , 0x00 }, - { STB0899_DISSTATUS , 0x20 }, - { STB0899_DISF22 , 0x8c }, - { STB0899_DISF22RX , 0x9a }, - { STB0899_SYSREG , 0x0b }, - { STB0899_ACRPRESC , 0x11 }, - { STB0899_ACRDIV1 , 0x0a }, - { STB0899_ACRDIV2 , 0x05 }, - { STB0899_DACR1 , 0x00 }, - { STB0899_DACR2 , 0x00 }, - { STB0899_OUTCFG , 0x00 }, - { STB0899_MODECFG , 0x00 }, - { STB0899_IRQSTATUS_3 , 0x30 }, - { STB0899_IRQSTATUS_2 , 0x00 }, - { STB0899_IRQSTATUS_1 , 0x00 }, - { STB0899_IRQSTATUS_0 , 0x00 }, - { STB0899_IRQMSK_3 , 0xf3 }, - { STB0899_IRQMSK_2 , 0xfc }, - { STB0899_IRQMSK_1 , 0xff }, - { STB0899_IRQMSK_0 , 0xff }, - { STB0899_IRQCFG , 0x00 }, - { STB0899_I2CCFG , 0x88 }, - { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ - { STB0899_IOPVALUE5 , 0x00 }, - { STB0899_IOPVALUE4 , 0x20 }, - { STB0899_IOPVALUE3 , 0xc9 }, - { STB0899_IOPVALUE2 , 0x90 }, - { STB0899_IOPVALUE1 , 0x40 }, - { STB0899_IOPVALUE0 , 0x00 }, - { STB0899_GPIO00CFG , 0x82 }, - { STB0899_GPIO01CFG , 0x82 }, - { STB0899_GPIO02CFG , 0x82 }, - { STB0899_GPIO03CFG , 0x82 }, - { STB0899_GPIO04CFG , 0x82 }, - { STB0899_GPIO05CFG , 0x82 }, - { STB0899_GPIO06CFG , 0x82 }, - { STB0899_GPIO07CFG , 0x82 }, - { STB0899_GPIO08CFG , 0x82 }, - { STB0899_GPIO09CFG , 0x82 }, - { STB0899_GPIO10CFG , 0x82 }, - { STB0899_GPIO11CFG , 0x82 }, - { STB0899_GPIO12CFG , 0x82 }, - { STB0899_GPIO13CFG , 0x82 }, - { STB0899_GPIO14CFG , 0x82 }, - { STB0899_GPIO15CFG , 0x82 }, - { STB0899_GPIO16CFG , 0x82 }, - { STB0899_GPIO17CFG , 0x82 }, - { STB0899_GPIO18CFG , 0x82 }, - { STB0899_GPIO19CFG , 0x82 }, - { STB0899_GPIO20CFG , 0x82 }, - { STB0899_SDATCFG , 0xb8 }, - { STB0899_SCLTCFG , 0xba }, - { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ - { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ - { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ - { STB0899_DIRCLKCFG , 0x82 }, - { STB0899_CLKOUT27CFG , 0x7e }, - { STB0899_STDBYCFG , 0x82 }, - { STB0899_CS0CFG , 0x82 }, - { STB0899_CS1CFG , 0x82 }, - { STB0899_DISEQCOCFG , 0x20 }, - { STB0899_GPIO32CFG , 0x82 }, - { STB0899_GPIO33CFG , 0x82 }, - { STB0899_GPIO34CFG , 0x82 }, - { STB0899_GPIO35CFG , 0x82 }, - { STB0899_GPIO36CFG , 0x82 }, - { STB0899_GPIO37CFG , 0x82 }, - { STB0899_GPIO38CFG , 0x82 }, - { STB0899_GPIO39CFG , 0x82 }, - { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ - { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ - { STB0899_FILTCTRL , 0x00 }, - { STB0899_SYSCTRL , 0x00 }, - { STB0899_STOPCLK1 , 0x20 }, - { STB0899_STOPCLK2 , 0x00 }, - { STB0899_INTBUFSTATUS , 0x00 }, - { STB0899_INTBUFCTRL , 0x0a }, - { 0xffff , 0xff }, -}; - -static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { - { STB0899_DEMOD , 0x00 }, - { STB0899_RCOMPC , 0xc9 }, - { STB0899_AGC1CN , 0x41 }, - { STB0899_AGC1REF , 0x08 }, - { STB0899_RTC , 0x7a }, - { STB0899_TMGCFG , 0x4e }, - { STB0899_AGC2REF , 0x33 }, - { STB0899_TLSR , 0x84 }, - { STB0899_CFD , 0xee }, - { STB0899_ACLC , 0x87 }, - { STB0899_BCLC , 0x94 }, - { STB0899_EQON , 0x41 }, - { STB0899_LDT , 0xdd }, - { STB0899_LDT2 , 0xc9 }, - { STB0899_EQUALREF , 0xb4 }, - { STB0899_TMGRAMP , 0x10 }, - { STB0899_TMGTHD , 0x30 }, - { STB0899_IDCCOMP , 0xfb }, - { STB0899_QDCCOMP , 0x03 }, - { STB0899_POWERI , 0x3b }, - { STB0899_POWERQ , 0x3d }, - { STB0899_RCOMP , 0x81 }, - { STB0899_AGCIQIN , 0x80 }, - { STB0899_AGC2I1 , 0x04 }, - { STB0899_AGC2I2 , 0xf5 }, - { STB0899_TLIR , 0x25 }, - { STB0899_RTF , 0x80 }, - { STB0899_DSTATUS , 0x00 }, - { STB0899_LDI , 0xca }, - { STB0899_CFRM , 0xf1 }, - { STB0899_CFRL , 0xf3 }, - { STB0899_NIRM , 0x2a }, - { STB0899_NIRL , 0x05 }, - { STB0899_ISYMB , 0x17 }, - { STB0899_QSYMB , 0xfa }, - { STB0899_SFRH , 0x2f }, - { STB0899_SFRM , 0x68 }, - { STB0899_SFRL , 0x40 }, - { STB0899_SFRUPH , 0x2f }, - { STB0899_SFRUPM , 0x68 }, - { STB0899_SFRUPL , 0x40 }, - { STB0899_EQUAI1 , 0xfd }, - { STB0899_EQUAQ1 , 0x04 }, - { STB0899_EQUAI2 , 0x0f }, - { STB0899_EQUAQ2 , 0xff }, - { STB0899_EQUAI3 , 0xdf }, - { STB0899_EQUAQ3 , 0xfa }, - { STB0899_EQUAI4 , 0x37 }, - { STB0899_EQUAQ4 , 0x0d }, - { STB0899_EQUAI5 , 0xbd }, - { STB0899_EQUAQ5 , 0xf7 }, - { STB0899_DSTATUS2 , 0x00 }, - { STB0899_VSTATUS , 0x00 }, - { STB0899_VERROR , 0xff }, - { STB0899_IQSWAP , 0x2a }, - { STB0899_ECNT1M , 0x00 }, - { STB0899_ECNT1L , 0x00 }, - { STB0899_ECNT2M , 0x00 }, - { STB0899_ECNT2L , 0x00 }, - { STB0899_ECNT3M , 0x00 }, - { STB0899_ECNT3L , 0x00 }, - { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, - { STB0899_VTH12 , 0xf0 }, - { STB0899_VTH23 , 0xa0 }, - { STB0899_VTH34 , 0x78 }, - { STB0899_VTH56 , 0x4e }, - { STB0899_VTH67 , 0x48 }, - { STB0899_VTH78 , 0x38 }, - { STB0899_PRVIT , 0xff }, - { STB0899_VITSYNC , 0x19 }, - { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ - { STB0899_TSULC , 0x42 }, - { STB0899_RSLLC , 0x40 }, - { STB0899_TSLPL , 0x12 }, - { STB0899_TSCFGH , 0x0c }, - { STB0899_TSCFGM , 0x00 }, - { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ - { STB0899_RSSYNCDEL , 0x00 }, - { STB0899_TSINHDELH , 0x02 }, - { STB0899_TSINHDELM , 0x00 }, - { STB0899_TSINHDELL , 0x00 }, - { STB0899_TSLLSTKM , 0x00 }, - { STB0899_TSLLSTKL , 0x00 }, - { STB0899_TSULSTKM , 0x00 }, - { STB0899_TSULSTKL , 0xab }, - { STB0899_PCKLENUL , 0x00 }, - { STB0899_PCKLENLL , 0xcc }, - { STB0899_RSPCKLEN , 0xcc }, - { STB0899_TSSTATUS , 0x80 }, - { STB0899_ERRCTRL1 , 0xb6 }, - { STB0899_ERRCTRL2 , 0x96 }, - { STB0899_ERRCTRL3 , 0x89 }, - { STB0899_DMONMSK1 , 0x27 }, - { STB0899_DMONMSK0 , 0x03 }, - { STB0899_DEMAPVIT , 0x5c }, - { STB0899_PLPARM , 0x1f }, - { STB0899_PDELCTRL , 0x48 }, - { STB0899_PDELCTRL2 , 0x00 }, - { STB0899_BBHCTRL1 , 0x00 }, - { STB0899_BBHCTRL2 , 0x00 }, - { STB0899_HYSTTHRESH , 0x77 }, - { STB0899_MATCSTM , 0x00 }, - { STB0899_MATCSTL , 0x00 }, - { STB0899_UPLCSTM , 0x00 }, - { STB0899_UPLCSTL , 0x00 }, - { STB0899_DFLCSTM , 0x00 }, - { STB0899_DFLCSTL , 0x00 }, - { STB0899_SYNCCST , 0x00 }, - { STB0899_SYNCDCSTM , 0x00 }, - { STB0899_SYNCDCSTL , 0x00 }, - { STB0899_ISI_ENTRY , 0x00 }, - { STB0899_ISI_BIT_EN , 0x00 }, - { STB0899_MATSTRM , 0x00 }, - { STB0899_MATSTRL , 0x00 }, - { STB0899_UPLSTRM , 0x00 }, - { STB0899_UPLSTRL , 0x00 }, - { STB0899_DFLSTRM , 0x00 }, - { STB0899_DFLSTRL , 0x00 }, - { STB0899_SYNCSTR , 0x00 }, - { STB0899_SYNCDSTRM , 0x00 }, - { STB0899_SYNCDSTRL , 0x00 }, - { STB0899_CFGPDELSTATUS1 , 0x10 }, - { STB0899_CFGPDELSTATUS2 , 0x00 }, - { STB0899_BBFERRORM , 0x00 }, - { STB0899_BBFERRORL , 0x00 }, - { STB0899_UPKTERRORM , 0x00 }, - { STB0899_UPKTERRORL , 0x00 }, - { 0xffff , 0xff }, -}; - -/* STB0899 demodulator config for the KNC1 and clones */ -static struct stb0899_config knc1_dvbs2_config = { - .init_dev = knc1_stb0899_s1_init_1, - .init_s2_demod = stb0899_s2_init_2, - .init_s1_demod = knc1_stb0899_s1_init_3, - .init_s2_fec = stb0899_s2_init_4, - .init_tst = stb0899_s1_init_5, - - .postproc = NULL, - - .demod_address = 0x68, -// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ - .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ -// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ - - .xtal_freq = 27000000, - .inversion = IQ_SWAP_OFF, - - .lo_clk = 76500000, - .hi_clk = 90000000, - - .esno_ave = STB0899_DVBS2_ESNO_AVE, - .esno_quant = STB0899_DVBS2_ESNO_QUANT, - .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, - .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, - .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, - .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, - .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, - .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, - .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, - - .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, - .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, - .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, - .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, - - .tuner_get_frequency = tda8261_get_frequency, - .tuner_set_frequency = tda8261_set_frequency, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = tda8261_get_bandwidth, - .tuner_set_rfsiggain = NULL -}; - -/* - * SD1878/SHA tuner config - * 1F, Single I/P, Horizontal mount, High Sensitivity - */ -static const struct tda8261_config sd1878c_config = { -// .name = "SD1878/SHA", - .addr = 0x60, - .step_size = TDA8261_STEP_1000 /* kHz */ -}; - -static u8 read_pwm(struct budget_av *budget_av) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, - {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} - }; - - if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) - || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -#define SUBID_DVBS_KNC1 0x0010 -#define SUBID_DVBS_KNC1_PLUS 0x0011 -#define SUBID_DVBS_TYPHOON 0x4f56 -#define SUBID_DVBS_CINERGY1200 0x1154 -#define SUBID_DVBS_CYNERGY1200N 0x1155 -#define SUBID_DVBS_TV_STAR 0x0014 -#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 -#define SUBID_DVBS_TV_STAR_CI 0x0016 -#define SUBID_DVBS2_KNC1 0x0018 -#define SUBID_DVBS2_KNC1_OEM 0x0019 -#define SUBID_DVBS_EASYWATCH_1 0x001a -#define SUBID_DVBS_EASYWATCH_2 0x001b -#define SUBID_DVBS2_EASYWATCH 0x001d -#define SUBID_DVBS_EASYWATCH 0x001e - -#define SUBID_DVBC_EASYWATCH 0x002a -#define SUBID_DVBC_EASYWATCH_MK3 0x002c -#define SUBID_DVBC_KNC1 0x0020 -#define SUBID_DVBC_KNC1_PLUS 0x0021 -#define SUBID_DVBC_KNC1_MK3 0x0022 -#define SUBID_DVBC_KNC1_TDA10024 0x0028 -#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 -#define SUBID_DVBC_CINERGY1200 0x1156 -#define SUBID_DVBC_CINERGY1200_MK3 0x1176 - -#define SUBID_DVBT_EASYWATCH 0x003a -#define SUBID_DVBT_KNC1_PLUS 0x0031 -#define SUBID_DVBT_KNC1 0x0030 -#define SUBID_DVBT_CINERGY1200 0x1157 - -static void frontend_init(struct budget_av *budget_av) -{ - struct saa7146_dev * saa = budget_av->budget.dev; - struct dvb_frontend * fe = NULL; - - /* Enable / PowerON Frontend */ - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); - - /* Wait for PowerON */ - msleep(100); - - /* additional setup necessary for the PLUS cards */ - switch (saa->pci->subsystem_device) { - case SUBID_DVBS_KNC1_PLUS: - case SUBID_DVBC_KNC1_PLUS: - case SUBID_DVBT_KNC1_PLUS: - case SUBID_DVBC_EASYWATCH: - case SUBID_DVBC_KNC1_PLUS_MK3: - case SUBID_DVBS2_KNC1: - case SUBID_DVBS2_KNC1_OEM: - case SUBID_DVBS2_EASYWATCH: - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); - break; - } - - switch (saa->pci->subsystem_device) { - - case SUBID_DVBS_KNC1: - /* - * maybe that setting is needed for other dvb-s cards as well, - * but so far it has been only confirmed for this type - */ - budget_av->reinitialise_demod = 1; - fallthrough; - case SUBID_DVBS_KNC1_PLUS: - case SUBID_DVBS_EASYWATCH_1: - if (saa->pci->subsystem_vendor == 0x1894) { - fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, - &budget_av->budget.i2c_adap); - if (fe) { - dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); - } - } else { - fe = dvb_attach(stv0299_attach, &typhoon_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - } - break; - - case SUBID_DVBS_TV_STAR: - case SUBID_DVBS_TV_STAR_PLUS_X4: - case SUBID_DVBS_TV_STAR_CI: - case SUBID_DVBS_CYNERGY1200N: - case SUBID_DVBS_EASYWATCH: - case SUBID_DVBS_EASYWATCH_2: - fe = dvb_attach(stv0299_attach, &philips_sd1878_config, - &budget_av->budget.i2c_adap); - if (fe) { - dvb_attach(dvb_pll_attach, fe, 0x60, - &budget_av->budget.i2c_adap, - DVB_PLL_PHILIPS_SD1878_TDA8261); - } - break; - - case SUBID_DVBS_TYPHOON: - fe = dvb_attach(stv0299_attach, &typhoon_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - break; - case SUBID_DVBS2_KNC1: - case SUBID_DVBS2_KNC1_OEM: - case SUBID_DVBS2_EASYWATCH: - budget_av->reinitialise_demod = 1; - if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) - dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); - - break; - case SUBID_DVBS_CINERGY1200: - fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; - } - break; - - case SUBID_DVBC_KNC1: - case SUBID_DVBC_KNC1_PLUS: - case SUBID_DVBC_CINERGY1200: - case SUBID_DVBC_EASYWATCH: - budget_av->reinitialise_demod = 1; - budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10021_attach, &philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe == NULL) - fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - - case SUBID_DVBC_EASYWATCH_MK3: - case SUBID_DVBC_CINERGY1200_MK3: - case SUBID_DVBC_KNC1_MK3: - case SUBID_DVBC_KNC1_TDA10024: - case SUBID_DVBC_KNC1_PLUS_MK3: - budget_av->reinitialise_demod = 1; - budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10023_attach, - &philips_cu1216_tda10023_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - - case SUBID_DVBT_EASYWATCH: - case SUBID_DVBT_KNC1: - case SUBID_DVBT_KNC1_PLUS: - case SUBID_DVBT_CINERGY1200: - budget_av->reinitialise_demod = 1; - fe = dvb_attach(tda10046_attach, &philips_tu1216_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops.tuner_ops.init = philips_tu1216_tuner_init; - fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; - } - break; - } - - if (fe == NULL) { - pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - saa->pci->vendor, - saa->pci->device, - saa->pci->subsystem_vendor, - saa->pci->subsystem_device); - return; - } - - budget_av->budget.dvb_frontend = fe; - - if (dvb_register_frontend(&budget_av->budget.dvb_adapter, - budget_av->budget.dvb_frontend)) { - pr_err("Frontend registration failed!\n"); - dvb_frontend_detach(budget_av->budget.dvb_frontend); - budget_av->budget.dvb_frontend = NULL; - } -} - - -static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) -{ - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; - - dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); - - if (*isr & MASK_10) - ttpci_budget_irq10_handler(dev, isr); -} - -static int budget_av_detach(struct saa7146_dev *dev) -{ - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; - int err; - - dprintk(2, "dev: %p\n", dev); - - if (1 == budget_av->has_saa7113) { - saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); - - msleep(200); - - saa7146_unregister_device(&budget_av->vd, dev); - - saa7146_vv_release(dev); - } - - if (budget_av->budget.ci_present) - ciintf_deinit(budget_av); - - if (budget_av->budget.dvb_frontend != NULL) { - dvb_unregister_frontend(budget_av->budget.dvb_frontend); - dvb_frontend_detach(budget_av->budget.dvb_frontend); - } - err = ttpci_budget_deinit(&budget_av->budget); - - kfree(budget_av); - - return err; -} - -#define KNC1_INPUTS 2 -static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { - { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, - V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, - V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, -}; - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); - if (i->index >= KNC1_INPUTS) - return -EINVAL; - memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; - - *i = budget_av->cur_input; - - dprintk(1, "VIDIOC_G_INPUT %d\n", *i); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; - - dprintk(1, "VIDIOC_S_INPUT %d\n", input); - return saa7113_setinput(budget_av, input); -} - -static struct saa7146_ext_vv vv_data; - -static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct budget_av *budget_av; - u8 *mac; - int err; - - dprintk(2, "dev: %p\n", dev); - - if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) - return -ENOMEM; - - budget_av->has_saa7113 = 0; - budget_av->budget.ci_present = 0; - - dev->ext_priv = budget_av; - - err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, - adapter_nr); - if (err) { - kfree(budget_av); - return err; - } - - /* knc1 initialization */ - saa7146_write(dev, DD1_STREAM_B, 0x04000000); - saa7146_write(dev, DD1_INIT, 0x07000600); - saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); - - if (saa7113_init(budget_av) == 0) { - budget_av->has_saa7113 = 1; - err = saa7146_vv_init(dev, &vv_data); - if (err != 0) { - /* fixme: proper cleanup here */ - ERR("cannot init vv subsystem\n"); - return err; - } - vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data.vid_ops.vidioc_g_input = vidioc_g_input; - vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - - if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) { - /* fixme: proper cleanup here */ - ERR("cannot register capture v4l2 device\n"); - saa7146_vv_release(dev); - return err; - } - - /* beware: this modifies dev->vv ... */ - saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, - SAA7146_HPS_SYNC_PORT_A); - - saa7113_setinput(budget_av, 0); - } - - /* fixme: find some sane values here... */ - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - - mac = budget_av->budget.dvb_adapter.proposed_mac; - if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { - pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", - budget_av->budget.dvb_adapter.num); - eth_zero_addr(mac); - } else { - pr_info("KNC1-%d: MAC addr = %pM\n", - budget_av->budget.dvb_adapter.num, mac); - } - - budget_av->budget.dvb_adapter.priv = budget_av; - frontend_init(budget_av); - ciintf_init(budget_av); - - ttpci_budget_init_hooks(&budget_av->budget); - - return 0; -} - -static struct saa7146_standard standard[] = { - {.name = "PAL",.id = V4L2_STD_PAL, - .v_offset = 0x17,.v_field = 288, - .h_offset = 0x14,.h_pixels = 680, - .v_max_out = 576,.h_max_out = 768 }, - - {.name = "NTSC",.id = V4L2_STD_NTSC, - .v_offset = 0x16,.v_field = 240, - .h_offset = 0x06,.h_pixels = 708, - .v_max_out = 480,.h_max_out = 640, }, -}; - -static struct saa7146_ext_vv vv_data = { - .inputs = 2, - .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 - .flags = 0, - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), -}; - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); -MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); -MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); -MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); -MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); -MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); -MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); -MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); -MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); -MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); -MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); -MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); -MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); -MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); -MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); -MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); -MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); -MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); -MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); -MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); -MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); -MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); -MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); -MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), - MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), - MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), - MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), - MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), - MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), - MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), - MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), - MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), - MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), - MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), - MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), - MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), - MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), - MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), - MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), - MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), - MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), - MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), - MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), - MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), - MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), - MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), - MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), - MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), - MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), - MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), - MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), - MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget_av", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = pci_tbl, - - .module = THIS_MODULE, - .attach = budget_av_attach, - .detach = budget_av_detach, - - .irq_mask = MASK_10, - .irq_func = budget_av_irq, -}; - -static int __init budget_av_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_av_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_av_init); -module_exit(budget_av_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c deleted file mode 100644 index d59d18647371..000000000000 --- a/drivers/media/pci/ttpci/budget-ci.c +++ /dev/null @@ -1,1574 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget-ci.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * msp430 IR support contributed by Jack Thomasson - * partially based on the Siemens DVB driver by Ralph+Marcus Metzler - * - * CI interface support (c) 2004 Andrew de Quincey - * - * the project's page is at https://linuxtv.org - */ - -#include -#include -#include -#include -#include -#include - -#include "budget.h" - -#include -#include "stv0299.h" -#include "stv0297.h" -#include "tda1004x.h" -#include "stb0899_drv.h" -#include "stb0899_reg.h" -#include "stb0899_cfg.h" -#include "stb6100.h" -#include "stb6100_cfg.h" -#include "lnbp21.h" -#include "bsbe1.h" -#include "bsru6.h" -#include "tda1002x.h" -#include "tda827x.h" -#include "bsbe1-d01a.h" - -#define MODULE_NAME "budget_ci" - -/* - * Regarding DEBIADDR_IR: - * Some CI modules hang if random addresses are read. - * Using address 0x4000 for the IR read means that we - * use the same address as for CI version, which should - * be a safe default. - */ -#define DEBIADDR_IR 0x4000 -#define DEBIADDR_CICONTROL 0x0000 -#define DEBIADDR_CIVERSION 0x4000 -#define DEBIADDR_IO 0x1000 -#define DEBIADDR_ATTR 0x3000 - -#define CICONTROL_RESET 0x01 -#define CICONTROL_ENABLETS 0x02 -#define CICONTROL_CAMDETECT 0x08 - -#define DEBICICTL 0x00420000 -#define DEBICICAM 0x02420000 - -#define SLOTSTATUS_NONE 1 -#define SLOTSTATUS_PRESENT 2 -#define SLOTSTATUS_RESET 4 -#define SLOTSTATUS_READY 8 -#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) - -/* RC5 device wildcard */ -#define IR_DEVICE_ANY 255 - -static int rc5_device = -1; -module_param(rc5_device, int, 0644); -MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); - -static int ir_debug; -module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -struct budget_ci_ir { - struct rc_dev *dev; - struct tasklet_struct msp430_irq_tasklet; - char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ - char phys[32]; - int rc5_device; - u32 ir_key; - bool have_command; - bool full_rc5; /* Outputs a full RC5 code */ -}; - -struct budget_ci { - struct budget budget; - struct tasklet_struct ciintf_irq_tasklet; - int slot_status; - int ci_irq; - struct dvb_ca_en50221 ca; - struct budget_ci_ir ir; - u8 tuner_pll_address; /* used for philips_tdm1316l configs */ -}; - -static void msp430_ir_interrupt(struct tasklet_struct *t) -{ - struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); - struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); - struct rc_dev *dev = budget_ci->ir.dev; - u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; - - /* - * The msp430 chip can generate two different bytes, command and device - * - * type1: X1CCCCCC, C = command bits (0 - 63) - * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit - * - * Each signal from the remote control can generate one or more command - * bytes and one or more device bytes. For the repeated bytes, the - * highest bit (X) is set. The first command byte is always generated - * before the first device byte. Other than that, no specific order - * seems to apply. To make life interesting, bytes can also be lost. - * - * Only when we have a command and device byte, a keypress is - * generated. - */ - - if (ir_debug) - printk("budget_ci: received byte 0x%02x\n", command); - - /* Remove repeat bit, we use every command */ - command = command & 0x7f; - - /* Is this a RC5 command byte? */ - if (command & 0x40) { - budget_ci->ir.have_command = true; - budget_ci->ir.ir_key = command & 0x3f; - return; - } - - /* It's a RC5 device byte */ - if (!budget_ci->ir.have_command) - return; - budget_ci->ir.have_command = false; - - if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && - budget_ci->ir.rc5_device != (command & 0x1f)) - return; - - if (budget_ci->ir.full_rc5) { - rc_keydown(dev, RC_PROTO_RC5, - RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key), - !!(command & 0x20)); - return; - } - - /* FIXME: We should generate complete scancodes for all devices */ - rc_keydown(dev, RC_PROTO_UNKNOWN, budget_ci->ir.ir_key, - !!(command & 0x20)); -} - -static int msp430_ir_init(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - struct rc_dev *dev; - int error; - - dev = rc_allocate_device(RC_DRIVER_SCANCODE); - if (!dev) { - printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); - return -ENOMEM; - } - - snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), - "Budget-CI dvb ir receiver %s", saa->name); - snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), - "pci-%s/ir0", pci_name(saa->pci)); - - dev->driver_name = MODULE_NAME; - dev->device_name = budget_ci->ir.name; - dev->input_phys = budget_ci->ir.phys; - dev->input_id.bustype = BUS_PCI; - dev->input_id.version = 1; - if (saa->pci->subsystem_vendor) { - dev->input_id.vendor = saa->pci->subsystem_vendor; - dev->input_id.product = saa->pci->subsystem_device; - } else { - dev->input_id.vendor = saa->pci->vendor; - dev->input_id.product = saa->pci->device; - } - dev->dev.parent = &saa->pci->dev; - - if (rc5_device < 0) - budget_ci->ir.rc5_device = IR_DEVICE_ANY; - else - budget_ci->ir.rc5_device = rc5_device; - - /* Select keymap and address */ - switch (budget_ci->budget.dev->pci->subsystem_device) { - case 0x100c: - case 0x100f: - case 0x1011: - case 0x1012: - /* The hauppauge keymap is a superset of these remotes */ - dev->map_name = RC_MAP_HAUPPAUGE; - budget_ci->ir.full_rc5 = true; - - if (rc5_device < 0) - budget_ci->ir.rc5_device = 0x1f; - break; - case 0x1010: - case 0x1017: - case 0x1019: - case 0x101a: - case 0x101b: - /* for the Technotrend 1500 bundled remote */ - dev->map_name = RC_MAP_TT_1500; - break; - default: - /* unknown remote */ - dev->map_name = RC_MAP_BUDGET_CI_OLD; - break; - } - if (!budget_ci->ir.full_rc5) - dev->scancode_mask = 0xff; - - error = rc_register_device(dev); - if (error) { - printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); - rc_free_device(dev); - return error; - } - - budget_ci->ir.dev = dev; - - tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); - - SAA7146_IER_ENABLE(saa, MASK_06); - saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); - - return 0; -} - -static void msp430_ir_deinit(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - - SAA7146_IER_DISABLE(saa, MASK_06); - saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - - rc_unregister_device(budget_ci->ir.dev); -} - -static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, - DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); -} - -static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, - DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); -} - -static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, - DEBIADDR_IO | (address & 3), 1, 1, 0); -} - -static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - - if (slot != 0) - return -EINVAL; - - return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, - DEBIADDR_IO | (address & 3), 1, value, 1, 0); -} - -static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - - if (slot != 0) - return -EINVAL; - - if (budget_ci->ci_irq) { - // trigger on RISING edge during reset so we know when READY is re-asserted - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - } - budget_ci->slot_status = SLOTSTATUS_RESET; - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); - msleep(1); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - return 0; -} - -static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - return 0; -} - -static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - struct saa7146_dev *saa = budget_ci->budget.dev; - int tmp; - - if (slot != 0) - return -EINVAL; - - saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); - - tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - tmp | CICONTROL_ENABLETS, 1, 0); - - ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - return 0; -} - -static void ciintf_interrupt(struct tasklet_struct *t) -{ - struct budget_ci *budget_ci = from_tasklet(budget_ci, t, - ciintf_irq_tasklet); - struct saa7146_dev *saa = budget_ci->budget.dev; - unsigned int flags; - - // ensure we don't get spurious IRQs during initialisation - if (!budget_ci->budget.ci_present) - return; - - // read the CAM status - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - if (flags & CICONTROL_CAMDETECT) { - - // GPIO should be set to trigger on falling edge if a CAM is present - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); - - if (budget_ci->slot_status & SLOTSTATUS_NONE) { - // CAM insertion IRQ - budget_ci->slot_status = SLOTSTATUS_PRESENT; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, - DVB_CA_EN50221_CAMCHANGE_INSERTED); - - } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { - // CAM ready (reset completed) - budget_ci->slot_status = SLOTSTATUS_READY; - dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); - - } else if (budget_ci->slot_status & SLOTSTATUS_READY) { - // FR/DA IRQ - dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); - } - } else { - - // trigger on rising edge if a CAM is not present - when a CAM is inserted, we - // only want to get the IRQ when it sets READY. If we trigger on the falling edge, - // the CAM might not actually be ready yet. - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - - // generate a CAM removal IRQ if we haven't already - if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { - // CAM removal IRQ - budget_ci->slot_status = SLOTSTATUS_NONE; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, - DVB_CA_EN50221_CAMCHANGE_REMOVED); - } - } -} - -static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; - unsigned int flags; - - // ensure we don't get spurious IRQs during initialisation - if (!budget_ci->budget.ci_present) - return -EINVAL; - - // read the CAM status - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - if (flags & CICONTROL_CAMDETECT) { - // mark it as present if it wasn't before - if (budget_ci->slot_status & SLOTSTATUS_NONE) { - budget_ci->slot_status = SLOTSTATUS_PRESENT; - } - - // during a RESET, we check if we can read from IO memory to see when CAM is ready - if (budget_ci->slot_status & SLOTSTATUS_RESET) { - if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { - budget_ci->slot_status = SLOTSTATUS_READY; - } - } - } else { - budget_ci->slot_status = SLOTSTATUS_NONE; - } - - if (budget_ci->slot_status != SLOTSTATUS_NONE) { - if (budget_ci->slot_status & SLOTSTATUS_READY) { - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - } - return DVB_CA_EN50221_POLL_CAM_PRESENT; - } - - return 0; -} - -static int ciintf_init(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - int flags; - int result; - int ci_version; - int ca_flags; - - memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); - - // enable DEBI pins - saa7146_write(saa, MC1, MASK_27 | MASK_11); - - // test if it is there - ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); - if ((ci_version & 0xa0) != 0xa0) { - result = -ENODEV; - goto error; - } - - // determine whether a CAM is present or not - flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); - budget_ci->slot_status = SLOTSTATUS_NONE; - if (flags & CICONTROL_CAMDETECT) - budget_ci->slot_status = SLOTSTATUS_PRESENT; - - // version 0xa2 of the CI firmware doesn't generate interrupts - if (ci_version == 0xa2) { - ca_flags = 0; - budget_ci->ci_irq = 0; - } else { - ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | - DVB_CA_EN50221_FLAG_IRQ_FR | - DVB_CA_EN50221_FLAG_IRQ_DA; - budget_ci->ci_irq = 1; - } - - // register CI interface - budget_ci->ca.owner = THIS_MODULE; - budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; - budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; - budget_ci->ca.read_cam_control = ciintf_read_cam_control; - budget_ci->ca.write_cam_control = ciintf_write_cam_control; - budget_ci->ca.slot_reset = ciintf_slot_reset; - budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; - budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; - budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; - budget_ci->ca.data = budget_ci; - if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, - &budget_ci->ca, - ca_flags, 1)) != 0) { - printk("budget_ci: CI interface detected, but initialisation failed.\n"); - goto error; - } - - // Setup CI slot IRQ - if (budget_ci->ci_irq) { - tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); - if (budget_ci->slot_status != SLOTSTATUS_NONE) { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); - } else { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); - } - SAA7146_IER_ENABLE(saa, MASK_03); - } - - // enable interface - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - // success! - printk("budget_ci: CI interface initialised\n"); - budget_ci->budget.ci_present = 1; - - // forge a fake CI IRQ so the CAM state is setup correctly - if (budget_ci->ci_irq) { - flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; - if (budget_ci->slot_status != SLOTSTATUS_NONE) - flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); - } - - return 0; - -error: - saa7146_write(saa, MC1, MASK_27); - return result; -} - -static void ciintf_deinit(struct budget_ci *budget_ci) -{ - struct saa7146_dev *saa = budget_ci->budget.dev; - - // disable CI interrupts - if (budget_ci->ci_irq) { - SAA7146_IER_DISABLE(saa, MASK_03); - saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ciintf_irq_tasklet); - } - - // reset interface - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); - msleep(1); - ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, - CICONTROL_RESET, 1, 0); - - // disable TS data stream to CI interface - saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); - - // release the CA device - dvb_ca_en50221_release(&budget_ci->ca); - - // disable DEBI pins - saa7146_write(saa, MC1, MASK_27); -} - -static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) -{ - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; - - dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); - - if (*isr & MASK_06) - tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); - - if (*isr & MASK_10) - ttpci_budget_irq10_handler(dev, isr); - - if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) - tasklet_schedule(&budget_ci->ciintf_irq_tasklet); -} - -static u8 philips_su1278_tt_inittab[] = { - 0x01, 0x0f, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x5b, - 0x05, 0x85, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0x02, - 0x09, 0x00, - 0x0C, 0x01, - 0x0D, 0x81, - 0x0E, 0x44, - 0x0f, 0x14, - 0x10, 0x3c, - 0x11, 0x84, - 0x12, 0xda, - 0x13, 0x97, - 0x14, 0x95, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x93, - 0xff, 0xff -}; - -static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) -{ - stv0299_writereg(fe, 0x0e, 0x44); - if (srate >= 10000000) { - stv0299_writereg(fe, 0x13, 0x97); - stv0299_writereg(fe, 0x14, 0x95); - stv0299_writereg(fe, 0x15, 0xc9); - stv0299_writereg(fe, 0x17, 0x8c); - stv0299_writereg(fe, 0x1a, 0xfe); - stv0299_writereg(fe, 0x1c, 0x7f); - stv0299_writereg(fe, 0x2d, 0x09); - } else { - stv0299_writereg(fe, 0x13, 0x99); - stv0299_writereg(fe, 0x14, 0x8d); - stv0299_writereg(fe, 0x15, 0xce); - stv0299_writereg(fe, 0x17, 0x43); - stv0299_writereg(fe, 0x1a, 0x1d); - stv0299_writereg(fe, 0x1c, 0x12); - stv0299_writereg(fe, 0x2d, 0x05); - } - stv0299_writereg(fe, 0x0e, 0x23); - stv0299_writereg(fe, 0x0f, 0x94); - stv0299_writereg(fe, 0x10, 0x39); - stv0299_writereg(fe, 0x15, 0xc9); - - stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg(fe, 0x21, (ratio) & 0xf0); - - return 0; -} - -static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u32 div; - u8 buf[4]; - struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; - - if ((p->frequency < 950000) || (p->frequency > 2150000)) - return -EINVAL; - - div = (p->frequency + (500 - 1)) / 500; /* round correctly */ - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; - buf[3] = 0x20; - - if (p->symbol_rate < 4000000) - buf[3] |= 1; - - if (p->frequency < 1250000) - buf[3] |= 0; - else if (p->frequency < 1550000) - buf[3] |= 0x40; - else if (p->frequency < 2050000) - buf[3] |= 0x80; - else if (p->frequency < 2150000) - buf[3] |= 0xC0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static const struct stv0299_config philips_su1278_tt_config = { - - .demod_address = 0x68, - .inittab = philips_su1278_tt_inittab, - .mclk = 64000000UL, - .invert = 0, - .skip_reinit = 1, - .lock_output = STV0299_LOCKOUTPUT_1, - .volt13_op0_op1 = STV0299_VOLT13_OP1, - .min_delay_ms = 50, - .set_symbol_rate = philips_su1278_tt_set_symbol_rate, -}; - - - -static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) -{ - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = - sizeof(td1316_init) }; - - // setup PLL configuration - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - msleep(1); - - // disable the mc44BC374c (do not check for errors) - tuner_msg.addr = 0x65; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); - } - - return 0; -} - -static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = p->frequency + 36130000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) - cp = 3; - else if (tuner_frequency < 160000000) - cp = 5; - else if (tuner_frequency < 200000000) - cp = 6; - else if (tuner_frequency < 290000000) - cp = 3; - else if (tuner_frequency < 420000000) - cp = 5; - else if (tuner_frequency < 480000000) - cp = 6; - else if (tuner_frequency < 620000000) - cp = 3; - else if (tuner_frequency < 830000000) - cp = 5; - else if (tuner_frequency < 895000000) - cp = 7; - else - return -EINVAL; - - // determine band - if (p->frequency < 49000000) - return -EINVAL; - else if (p->frequency < 159000000) - band = 1; - else if (p->frequency < 444000000) - band = 2; - else if (p->frequency < 861000000) - band = 4; - else - return -EINVAL; - - // setup PLL filter and TDA9889 - switch (p->bandwidth_hz) { - case 6000000: - tda1004x_writereg(fe, 0x0C, 0x14); - filter = 0; - break; - - case 7000000: - tda1004x_writereg(fe, 0x0C, 0x80); - filter = 0; - break; - - case 8000000: - tda1004x_writereg(fe, 0x0C, 0x14); - filter = 1; - break; - - default: - return -EINVAL; - } - - // calculate divisor - // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - return 0; -} - -static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - - return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); -} - -static struct tda1004x_config philips_tdm1316l_config = { - - .demod_address = 0x8, - .invert = 0, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tdm1316l_request_firmware, -}; - -static struct tda1004x_config philips_tdm1316l_config_invert = { - - .demod_address = 0x8, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_4M, - .agc_config = TDA10046_AGC_DEFAULT, - .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tdm1316l_request_firmware, -}; - -static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; - u8 tuner_buf[5]; - struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, - .flags = 0, - .buf = tuner_buf, - .len = sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - - // determine charge pump - tuner_frequency = p->frequency + 36125000; - if (tuner_frequency < 87000000) - return -EINVAL; - else if (tuner_frequency < 130000000) { - cp = 3; - band = 1; - } else if (tuner_frequency < 160000000) { - cp = 5; - band = 1; - } else if (tuner_frequency < 200000000) { - cp = 6; - band = 1; - } else if (tuner_frequency < 290000000) { - cp = 3; - band = 2; - } else if (tuner_frequency < 420000000) { - cp = 5; - band = 2; - } else if (tuner_frequency < 480000000) { - cp = 6; - band = 2; - } else if (tuner_frequency < 620000000) { - cp = 3; - band = 4; - } else if (tuner_frequency < 830000000) { - cp = 5; - band = 4; - } else if (tuner_frequency < 895000000) { - cp = 7; - band = 4; - } else - return -EINVAL; - - // assume PLL filter should always be 8MHz for the moment. - filter = 1; - - // calculate divisor - tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xc8; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - tuner_buf[4] = 0x80; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(50); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(1); - - return 0; -} - -static u8 dvbc_philips_tdm1316l_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x09, - 0x01, 0x69, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x20, 0x00, - 0x21, 0x40, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x40, - 0x25, 0x88, - 0x30, 0xff, - 0x31, 0x00, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x50, - 0x35, 0x7f, - 0x36, 0x00, - 0x37, 0x20, - 0x38, 0x00, - 0x40, 0x1c, - 0x41, 0xff, - 0x42, 0x29, - 0x43, 0x20, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x00, - 0x4b, 0x7b, - 0x52, 0x30, - 0x55, 0xae, - 0x56, 0x47, - 0x57, 0xe1, - 0x58, 0x3a, - 0x5a, 0x1e, - 0x5b, 0x34, - 0x60, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x02, - 0x6b, 0x00, - 0x70, 0xff, - 0x71, 0x00, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x00, - 0x81, 0x00, - 0x82, 0x00, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x80, - 0x86, 0x24, - 0x87, 0x78, - 0x88, 0x10, - 0x89, 0x00, - 0x90, 0x01, - 0x91, 0x01, - 0xa0, 0x04, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x53, - 0xc1, 0x70, - 0xc2, 0x12, - 0xd0, 0x00, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x00, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x00, - 0x61, 0x38, - 0x62, 0x0a, - 0x53, 0x13, - 0x59, 0x08, - 0xff, 0xff, -}; - -static struct stv0297_config dvbc_philips_tdm1316l_config = { - .demod_address = 0x1c, - .inittab = dvbc_philips_tdm1316l_inittab, - .invert = 0, - .stop_during_read = 1, -}; - -static struct tda10023_config tda10023_config = { - .demod_address = 0xc, - .invert = 0, - .xtal = 16000000, - .pll_m = 11, - .pll_p = 3, - .pll_n = 1, - .deltaf = 0xa511, -}; - -static struct tda827x_config tda827x_config = { - .config = 0, -}; - -/* TT S2-3200 DVB-S (STB0899) Inittab */ -static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { - - { STB0899_DEV_ID , 0x81 }, - { STB0899_DISCNTRL1 , 0x32 }, - { STB0899_DISCNTRL2 , 0x80 }, - { STB0899_DISRX_ST0 , 0x04 }, - { STB0899_DISRX_ST1 , 0x00 }, - { STB0899_DISPARITY , 0x00 }, - { STB0899_DISSTATUS , 0x20 }, - { STB0899_DISF22 , 0x8c }, - { STB0899_DISF22RX , 0x9a }, - { STB0899_SYSREG , 0x0b }, - { STB0899_ACRPRESC , 0x11 }, - { STB0899_ACRDIV1 , 0x0a }, - { STB0899_ACRDIV2 , 0x05 }, - { STB0899_DACR1 , 0x00 }, - { STB0899_DACR2 , 0x00 }, - { STB0899_OUTCFG , 0x00 }, - { STB0899_MODECFG , 0x00 }, - { STB0899_IRQSTATUS_3 , 0x30 }, - { STB0899_IRQSTATUS_2 , 0x00 }, - { STB0899_IRQSTATUS_1 , 0x00 }, - { STB0899_IRQSTATUS_0 , 0x00 }, - { STB0899_IRQMSK_3 , 0xf3 }, - { STB0899_IRQMSK_2 , 0xfc }, - { STB0899_IRQMSK_1 , 0xff }, - { STB0899_IRQMSK_0 , 0xff }, - { STB0899_IRQCFG , 0x00 }, - { STB0899_I2CCFG , 0x88 }, - { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ - { STB0899_IOPVALUE5 , 0x00 }, - { STB0899_IOPVALUE4 , 0x20 }, - { STB0899_IOPVALUE3 , 0xc9 }, - { STB0899_IOPVALUE2 , 0x90 }, - { STB0899_IOPVALUE1 , 0x40 }, - { STB0899_IOPVALUE0 , 0x00 }, - { STB0899_GPIO00CFG , 0x82 }, - { STB0899_GPIO01CFG , 0x82 }, - { STB0899_GPIO02CFG , 0x82 }, - { STB0899_GPIO03CFG , 0x82 }, - { STB0899_GPIO04CFG , 0x82 }, - { STB0899_GPIO05CFG , 0x82 }, - { STB0899_GPIO06CFG , 0x82 }, - { STB0899_GPIO07CFG , 0x82 }, - { STB0899_GPIO08CFG , 0x82 }, - { STB0899_GPIO09CFG , 0x82 }, - { STB0899_GPIO10CFG , 0x82 }, - { STB0899_GPIO11CFG , 0x82 }, - { STB0899_GPIO12CFG , 0x82 }, - { STB0899_GPIO13CFG , 0x82 }, - { STB0899_GPIO14CFG , 0x82 }, - { STB0899_GPIO15CFG , 0x82 }, - { STB0899_GPIO16CFG , 0x82 }, - { STB0899_GPIO17CFG , 0x82 }, - { STB0899_GPIO18CFG , 0x82 }, - { STB0899_GPIO19CFG , 0x82 }, - { STB0899_GPIO20CFG , 0x82 }, - { STB0899_SDATCFG , 0xb8 }, - { STB0899_SCLTCFG , 0xba }, - { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ - { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ - { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ - { STB0899_DIRCLKCFG , 0x82 }, - { STB0899_CLKOUT27CFG , 0x7e }, - { STB0899_STDBYCFG , 0x82 }, - { STB0899_CS0CFG , 0x82 }, - { STB0899_CS1CFG , 0x82 }, - { STB0899_DISEQCOCFG , 0x20 }, - { STB0899_GPIO32CFG , 0x82 }, - { STB0899_GPIO33CFG , 0x82 }, - { STB0899_GPIO34CFG , 0x82 }, - { STB0899_GPIO35CFG , 0x82 }, - { STB0899_GPIO36CFG , 0x82 }, - { STB0899_GPIO37CFG , 0x82 }, - { STB0899_GPIO38CFG , 0x82 }, - { STB0899_GPIO39CFG , 0x82 }, - { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ - { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ - { STB0899_FILTCTRL , 0x00 }, - { STB0899_SYSCTRL , 0x00 }, - { STB0899_STOPCLK1 , 0x20 }, - { STB0899_STOPCLK2 , 0x00 }, - { STB0899_INTBUFSTATUS , 0x00 }, - { STB0899_INTBUFCTRL , 0x0a }, - { 0xffff , 0xff }, -}; - -static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { - { STB0899_DEMOD , 0x00 }, - { STB0899_RCOMPC , 0xc9 }, - { STB0899_AGC1CN , 0x41 }, - { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x7a }, - { STB0899_TMGCFG , 0x4e }, - { STB0899_AGC2REF , 0x34 }, - { STB0899_TLSR , 0x84 }, - { STB0899_CFD , 0xc7 }, - { STB0899_ACLC , 0x87 }, - { STB0899_BCLC , 0x94 }, - { STB0899_EQON , 0x41 }, - { STB0899_LDT , 0xdd }, - { STB0899_LDT2 , 0xc9 }, - { STB0899_EQUALREF , 0xb4 }, - { STB0899_TMGRAMP , 0x10 }, - { STB0899_TMGTHD , 0x30 }, - { STB0899_IDCCOMP , 0xfb }, - { STB0899_QDCCOMP , 0x03 }, - { STB0899_POWERI , 0x3b }, - { STB0899_POWERQ , 0x3d }, - { STB0899_RCOMP , 0x81 }, - { STB0899_AGCIQIN , 0x80 }, - { STB0899_AGC2I1 , 0x04 }, - { STB0899_AGC2I2 , 0xf5 }, - { STB0899_TLIR , 0x25 }, - { STB0899_RTF , 0x80 }, - { STB0899_DSTATUS , 0x00 }, - { STB0899_LDI , 0xca }, - { STB0899_CFRM , 0xf1 }, - { STB0899_CFRL , 0xf3 }, - { STB0899_NIRM , 0x2a }, - { STB0899_NIRL , 0x05 }, - { STB0899_ISYMB , 0x17 }, - { STB0899_QSYMB , 0xfa }, - { STB0899_SFRH , 0x2f }, - { STB0899_SFRM , 0x68 }, - { STB0899_SFRL , 0x40 }, - { STB0899_SFRUPH , 0x2f }, - { STB0899_SFRUPM , 0x68 }, - { STB0899_SFRUPL , 0x40 }, - { STB0899_EQUAI1 , 0xfd }, - { STB0899_EQUAQ1 , 0x04 }, - { STB0899_EQUAI2 , 0x0f }, - { STB0899_EQUAQ2 , 0xff }, - { STB0899_EQUAI3 , 0xdf }, - { STB0899_EQUAQ3 , 0xfa }, - { STB0899_EQUAI4 , 0x37 }, - { STB0899_EQUAQ4 , 0x0d }, - { STB0899_EQUAI5 , 0xbd }, - { STB0899_EQUAQ5 , 0xf7 }, - { STB0899_DSTATUS2 , 0x00 }, - { STB0899_VSTATUS , 0x00 }, - { STB0899_VERROR , 0xff }, - { STB0899_IQSWAP , 0x2a }, - { STB0899_ECNT1M , 0x00 }, - { STB0899_ECNT1L , 0x00 }, - { STB0899_ECNT2M , 0x00 }, - { STB0899_ECNT2L , 0x00 }, - { STB0899_ECNT3M , 0x00 }, - { STB0899_ECNT3L , 0x00 }, - { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, - { STB0899_VTH12 , 0xf0 }, - { STB0899_VTH23 , 0xa0 }, - { STB0899_VTH34 , 0x78 }, - { STB0899_VTH56 , 0x4e }, - { STB0899_VTH67 , 0x48 }, - { STB0899_VTH78 , 0x38 }, - { STB0899_PRVIT , 0xff }, - { STB0899_VITSYNC , 0x19 }, - { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ - { STB0899_TSULC , 0x42 }, - { STB0899_RSLLC , 0x40 }, - { STB0899_TSLPL , 0x12 }, - { STB0899_TSCFGH , 0x0c }, - { STB0899_TSCFGM , 0x00 }, - { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ - { STB0899_RSSYNCDEL , 0x00 }, - { STB0899_TSINHDELH , 0x02 }, - { STB0899_TSINHDELM , 0x00 }, - { STB0899_TSINHDELL , 0x00 }, - { STB0899_TSLLSTKM , 0x00 }, - { STB0899_TSLLSTKL , 0x00 }, - { STB0899_TSULSTKM , 0x00 }, - { STB0899_TSULSTKL , 0xab }, - { STB0899_PCKLENUL , 0x00 }, - { STB0899_PCKLENLL , 0xcc }, - { STB0899_RSPCKLEN , 0xcc }, - { STB0899_TSSTATUS , 0x80 }, - { STB0899_ERRCTRL1 , 0xb6 }, - { STB0899_ERRCTRL2 , 0x96 }, - { STB0899_ERRCTRL3 , 0x89 }, - { STB0899_DMONMSK1 , 0x27 }, - { STB0899_DMONMSK0 , 0x03 }, - { STB0899_DEMAPVIT , 0x5c }, - { STB0899_PLPARM , 0x1f }, - { STB0899_PDELCTRL , 0x48 }, - { STB0899_PDELCTRL2 , 0x00 }, - { STB0899_BBHCTRL1 , 0x00 }, - { STB0899_BBHCTRL2 , 0x00 }, - { STB0899_HYSTTHRESH , 0x77 }, - { STB0899_MATCSTM , 0x00 }, - { STB0899_MATCSTL , 0x00 }, - { STB0899_UPLCSTM , 0x00 }, - { STB0899_UPLCSTL , 0x00 }, - { STB0899_DFLCSTM , 0x00 }, - { STB0899_DFLCSTL , 0x00 }, - { STB0899_SYNCCST , 0x00 }, - { STB0899_SYNCDCSTM , 0x00 }, - { STB0899_SYNCDCSTL , 0x00 }, - { STB0899_ISI_ENTRY , 0x00 }, - { STB0899_ISI_BIT_EN , 0x00 }, - { STB0899_MATSTRM , 0x00 }, - { STB0899_MATSTRL , 0x00 }, - { STB0899_UPLSTRM , 0x00 }, - { STB0899_UPLSTRL , 0x00 }, - { STB0899_DFLSTRM , 0x00 }, - { STB0899_DFLSTRL , 0x00 }, - { STB0899_SYNCSTR , 0x00 }, - { STB0899_SYNCDSTRM , 0x00 }, - { STB0899_SYNCDSTRL , 0x00 }, - { STB0899_CFGPDELSTATUS1 , 0x10 }, - { STB0899_CFGPDELSTATUS2 , 0x00 }, - { STB0899_BBFERRORM , 0x00 }, - { STB0899_BBFERRORL , 0x00 }, - { STB0899_UPKTERRORM , 0x00 }, - { STB0899_UPKTERRORL , 0x00 }, - { 0xffff , 0xff }, -}; - -static struct stb0899_config tt3200_config = { - .init_dev = tt3200_stb0899_s1_init_1, - .init_s2_demod = stb0899_s2_init_2, - .init_s1_demod = tt3200_stb0899_s1_init_3, - .init_s2_fec = stb0899_s2_init_4, - .init_tst = stb0899_s1_init_5, - - .postproc = NULL, - - .demod_address = 0x68, - - .xtal_freq = 27000000, - .inversion = IQ_SWAP_ON, - - .lo_clk = 76500000, - .hi_clk = 99000000, - - .esno_ave = STB0899_DVBS2_ESNO_AVE, - .esno_quant = STB0899_DVBS2_ESNO_QUANT, - .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, - .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, - .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, - .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, - .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, - .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, - .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, - - .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, - .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, - .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, - .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, - - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, - .tuner_set_rfsiggain = NULL -}; - -static struct stb6100_config tt3200_stb6100_config = { - .tuner_address = 0x60, - .refclock = 27000000, -}; - -static void frontend_init(struct budget_ci *budget_ci) -{ - switch (budget_ci->budget.dev->pci->subsystem_device) { - case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) - budget_ci->budget.dvb_frontend = - dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; - break; - } - break; - - case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) - budget_ci->budget.dvb_frontend = - dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; - break; - } - break; - - case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) - budget_ci->tuner_pll_address = 0x61; - budget_ci->budget.dvb_frontend = - dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) - budget_ci->tuner_pll_address = 0x63; - budget_ci->budget.dvb_frontend = - dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) - budget_ci->tuner_pll_address = 0x60; - budget_ci->budget.dvb_frontend = - dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; - break; - } - break; - - case 0x1017: // TT S-1500 PCI - budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; - - budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ - budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { - printk(KERN_ERR "%s: No tda827x found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ - budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { - if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { - printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } else { - printk(KERN_ERR "%s: No STB6000 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - case 0x1019: // TT S2-3200 PCI - /* - * NOTE! on some STB0899 versions, the internal PLL takes a longer time - * to settle, aka LOCK. On the older revisions of the chip, we don't see - * this, as a result on the newer chips the entire clock tree, will not - * be stable after a freshly POWER 'ed up situation. - * In this case, we should RESET the STB0899 (Active LOW) and wait for - * PLL stabilization. - * - * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is - * connected to the SAA7146 GPIO, GPIO2, Pin 142 - */ - /* Reset Demodulator */ - saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); - /* Wait for everything to die */ - msleep(50); - /* Pull it up out of Reset state */ - saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); - /* Wait for PLL to stabilize */ - msleep(250); - /* - * PLL state should be stable now. Ideally, we should check - * for PLL LOCK status. But well, never mind! - */ - budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); - if (budget_ci->budget.dvb_frontend) { - if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { - if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { - printk("%s: No LNBP21 found!\n", __func__); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } else { - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } - break; - - } - - if (budget_ci->budget.dvb_frontend == NULL) { - printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget_ci->budget.dev->pci->vendor, - budget_ci->budget.dev->pci->device, - budget_ci->budget.dev->pci->subsystem_vendor, - budget_ci->budget.dev->pci->subsystem_device); - } else { - if (dvb_register_frontend - (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { - printk("budget-ci: Frontend registration failed!\n"); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - budget_ci->budget.dvb_frontend = NULL; - } - } -} - -static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) -{ - struct budget_ci *budget_ci; - int err; - - budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); - if (!budget_ci) { - err = -ENOMEM; - goto out1; - } - - dprintk(2, "budget_ci: %p\n", budget_ci); - - dev->ext_priv = budget_ci; - - err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, - adapter_nr); - if (err) - goto out2; - - err = msp430_ir_init(budget_ci); - if (err) - goto out3; - - ciintf_init(budget_ci); - - budget_ci->budget.dvb_adapter.priv = budget_ci; - frontend_init(budget_ci); - - ttpci_budget_init_hooks(&budget_ci->budget); - - return 0; - -out3: - ttpci_budget_deinit(&budget_ci->budget); -out2: - kfree(budget_ci); -out1: - return err; -} - -static int budget_ci_detach(struct saa7146_dev *dev) -{ - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; - struct saa7146_dev *saa = budget_ci->budget.dev; - int err; - - if (budget_ci->budget.ci_present) - ciintf_deinit(budget_ci); - msp430_ir_deinit(budget_ci); - if (budget_ci->budget.dvb_frontend) { - dvb_unregister_frontend(budget_ci->budget.dvb_frontend); - dvb_frontend_detach(budget_ci->budget.dvb_frontend); - } - err = ttpci_budget_deinit(&budget_ci->budget); - - // disable frontend and CI interface - saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); - - kfree(budget_ci); - - return err; -} - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); -MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), - MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), - MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), - MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), - MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), - MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), - MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), - MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), - MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget_ci dvb", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = &pci_tbl[0], - .attach = budget_ci_attach, - .detach = budget_ci_detach, - - .irq_mask = MASK_03 | MASK_06 | MASK_10, - .irq_func = budget_ci_irq, -}; - -static int __init budget_ci_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_ci_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_ci_init); -module_exit(budget_ci_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards w/ CI-module produced by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c deleted file mode 100644 index 5d5796f24469..000000000000 --- a/drivers/media/pci/ttpci/budget-core.c +++ /dev/null @@ -1,603 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget-core.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * 26feb2004 Support for FS Activy Card (Grundig tuner) by - * Michael Dreher , - * Oliver Endriss , - * Andreas 'randy' Weinberger - * - * the project's page is at https://linuxtv.org - */ - - -#include "budget.h" -#include "ttpci-eeprom.h" - -#define TS_WIDTH (2 * TS_SIZE) -#define TS_WIDTH_ACTIVY TS_SIZE -#define TS_WIDTH_DVBC TS_SIZE -#define TS_HEIGHT_MASK 0xf00 -#define TS_HEIGHT_MASK_ACTIVY 0xc00 -#define TS_HEIGHT_MASK_DVBC 0xe00 -#define TS_MIN_BUFSIZE_K 188 -#define TS_MAX_BUFSIZE_K 1410 -#define TS_MAX_BUFSIZE_K_ACTIVY 564 -#define TS_MAX_BUFSIZE_K_DVBC 1316 -#define BUFFER_WARNING_WAIT (30*HZ) - -int budget_debug; -static int dma_buffer_size = TS_MIN_BUFSIZE_K; -module_param_named(debug, budget_debug, int, 0644); -module_param_named(bufsize, dma_buffer_size, int, 0444); -MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); -MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); - -/**************************************************************************** - * TT budget / WinTV Nova - ****************************************************************************/ - -static int stop_ts_capture(struct budget *budget) -{ - dprintk(2, "budget: %p\n", budget); - - saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off - SAA7146_IER_DISABLE(budget->dev, MASK_10); - return 0; -} - -static int start_ts_capture(struct budget *budget) -{ - struct saa7146_dev *dev = budget->dev; - - dprintk(2, "budget: %p\n", budget); - - if (!budget->feeding || !budget->fe_synced) - return 0; - - saa7146_write(dev, MC1, MASK_20); // DMA3 off - - memset(budget->grabbing, 0x00, budget->buffer_size); - - saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - - budget->ttbp = 0; - - /* - * Signal path on the Activy: - * - * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory - * - * Since the tuner feeds 204 bytes packets into the SAA7146, - * DMA3 is configured to strip the trailing 16 FEC bytes: - * Pitch: 188, NumBytes3: 188, NumLines3: 1024 - */ - - switch(budget->card->type) { - case BUDGET_FS_ACTIVY: - saa7146_write(dev, DD1_INIT, 0x04000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - break; - case BUDGET_PATCH: - saa7146_write(dev, DD1_INIT, 0x00000200); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - break; - case BUDGET_CIN1200C_MK3: - case BUDGET_KNC1C_MK3: - case BUDGET_KNC1C_TDA10024: - case BUDGET_KNC1CP_MK3: - if (budget->video_port == BUDGET_VIDEO_PORTA) { - saa7146_write(dev, DD1_INIT, 0x06000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - } else { - saa7146_write(dev, DD1_INIT, 0x00000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - } - break; - default: - if (budget->video_port == BUDGET_VIDEO_PORTA) { - saa7146_write(dev, DD1_INIT, 0x06000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x00000000); - } else { - saa7146_write(dev, DD1_INIT, 0x02000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - } - } - - saa7146_write(dev, MC2, (MASK_08 | MASK_24)); - mdelay(10); - - saa7146_write(dev, BASE_ODD3, 0); - if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { - // using odd/even buffers - saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); - } else { - // using a single buffer - saa7146_write(dev, BASE_EVEN3, 0); - } - saa7146_write(dev, PROT_ADDR3, budget->buffer_size); - saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); - - saa7146_write(dev, PITCH3, budget->buffer_width); - saa7146_write(dev, NUM_LINE_BYTE3, - (budget->buffer_height << 16) | budget->buffer_width); - - saa7146_write(dev, MC2, (MASK_04 | MASK_20)); - - SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ - SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ - saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ - - return 0; -} - -static int budget_read_fe_status(struct dvb_frontend *fe, - enum fe_status *status) -{ - struct budget *budget = (struct budget *) fe->dvb->priv; - int synced; - int ret; - - if (budget->read_fe_status) - ret = budget->read_fe_status(fe, status); - else - ret = -EINVAL; - - if (!ret) { - synced = (*status & FE_HAS_LOCK); - if (synced != budget->fe_synced) { - budget->fe_synced = synced; - spin_lock(&budget->feedlock); - if (synced) - start_ts_capture(budget); - else - stop_ts_capture(budget); - spin_unlock(&budget->feedlock); - } - } - return ret; -} - -static void vpeirq(struct tasklet_struct *t) -{ - struct budget *budget = from_tasklet(budget, t, vpe_tasklet); - u8 *mem = (u8 *) (budget->grabbing); - u32 olddma = budget->ttbp; - u32 newdma = saa7146_read(budget->dev, PCI_VDP3); - u32 count; - - /* Ensure streamed PCI data is synced to CPU */ - dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, - budget->pt.nents, DMA_FROM_DEVICE); - - /* nearest lower position divisible by 188 */ - newdma -= newdma % 188; - - if (newdma >= budget->buffer_size) - return; - - budget->ttbp = newdma; - - if (budget->feeding == 0 || newdma == olddma) - return; - - if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ - count = newdma - olddma; - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); - } else { /* wraparound, dump olddma..buflen and 0..newdma */ - count = budget->buffer_size - olddma; - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); - count += newdma; - dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); - } - - if (count > budget->buffer_warning_threshold) - budget->buffer_warnings++; - - if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { - printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", - budget->dev->name, __func__, budget->buffer_warnings, count); - budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; - budget->buffer_warnings = 0; - } -} - - -static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config, - int addr, int count, int nobusyloop) -{ - struct saa7146_dev *saa = budget->dev; - int result; - - result = saa7146_wait_for_debi_done(saa, nobusyloop); - if (result < 0) - return result; - - saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, MC2, (2 << 16) | 2); - - result = saa7146_wait_for_debi_done(saa, nobusyloop); - if (result < 0) - return result; - - result = saa7146_read(saa, DEBI_AD); - result &= (0xffffffffUL >> ((4 - count) * 8)); - return result; -} - -int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop) -{ - if (count > 4 || count <= 0) - return 0; - - if (uselocks) { - unsigned long flags; - int result; - - spin_lock_irqsave(&budget->debilock, flags); - result = ttpci_budget_debiread_nolock(budget, config, addr, - count, nobusyloop); - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - return ttpci_budget_debiread_nolock(budget, config, addr, - count, nobusyloop); -} - -static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config, - int addr, int count, u32 value, int nobusyloop) -{ - struct saa7146_dev *saa = budget->dev; - int result; - - result = saa7146_wait_for_debi_done(saa, nobusyloop); - if (result < 0) - return result; - - saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); - saa7146_write(saa, DEBI_CONFIG, config); - saa7146_write(saa, DEBI_PAGE, 0); - saa7146_write(saa, DEBI_AD, value); - saa7146_write(saa, MC2, (2 << 16) | 2); - - result = saa7146_wait_for_debi_done(saa, nobusyloop); - return result < 0 ? result : 0; -} - -int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, - int count, u32 value, int uselocks, int nobusyloop) -{ - if (count > 4 || count <= 0) - return 0; - - if (uselocks) { - unsigned long flags; - int result; - - spin_lock_irqsave(&budget->debilock, flags); - result = ttpci_budget_debiwrite_nolock(budget, config, addr, - count, value, nobusyloop); - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } - return ttpci_budget_debiwrite_nolock(budget, config, addr, - count, value, nobusyloop); -} - - -/**************************************************************************** - * DVB API SECTION - ****************************************************************************/ - -static int budget_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct budget *budget = (struct budget *) demux->priv; - int status = 0; - - dprintk(2, "budget: %p\n", budget); - - if (!demux->dmx.frontend) - return -EINVAL; - - spin_lock(&budget->feedlock); - feed->pusi_seen = false; /* have a clean section start */ - if (budget->feeding++ == 0) - status = start_ts_capture(budget); - spin_unlock(&budget->feedlock); - return status; -} - -static int budget_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct budget *budget = (struct budget *) demux->priv; - int status = 0; - - dprintk(2, "budget: %p\n", budget); - - spin_lock(&budget->feedlock); - if (--budget->feeding == 0) - status = stop_ts_capture(budget); - spin_unlock(&budget->feedlock); - return status; -} - -static int budget_register(struct budget *budget) -{ - struct dvb_demux *dvbdemux = &budget->demux; - int ret; - - dprintk(2, "budget: %p\n", budget); - - dvbdemux->priv = (void *) budget; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = budget_start_feed; - dvbdemux->stop_feed = budget_stop_feed; - dvbdemux->write_to_decoder = NULL; - - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&budget->demux); - - budget->dmxdev.filternum = 256; - budget->dmxdev.demux = &dvbdemux->dmx; - budget->dmxdev.capabilities = 0; - - dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); - - budget->hw_frontend.source = DMX_FRONTEND_0; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); - - if (ret < 0) - goto err_release_dmx; - - budget->mem_frontend.source = DMX_MEMORY_FE; - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); - if (ret < 0) - goto err_release_dmx; - - ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); - if (ret < 0) - goto err_release_dmx; - - dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); - - return 0; - -err_release_dmx: - dvb_dmxdev_release(&budget->dmxdev); - dvb_dmx_release(&budget->demux); - return ret; -} - -static void budget_unregister(struct budget *budget) -{ - struct dvb_demux *dvbdemux = &budget->demux; - - dprintk(2, "budget: %p\n", budget); - - dvb_net_release(&budget->dvb_net); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); - - dvb_dmxdev_release(&budget->dmxdev); - dvb_dmx_release(&budget->demux); -} - -int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, - struct saa7146_pci_extension_data *info, - struct module *owner, short *adapter_nums) -{ - int ret = 0; - struct budget_info *bi = info->ext_priv; - int max_bufsize; - int height_mask; - - memset(budget, 0, sizeof(struct budget)); - - dprintk(2, "dev: %p, budget: %p\n", dev, budget); - - budget->card = bi; - budget->dev = (struct saa7146_dev *) dev; - - switch(budget->card->type) { - case BUDGET_FS_ACTIVY: - budget->buffer_width = TS_WIDTH_ACTIVY; - max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; - height_mask = TS_HEIGHT_MASK_ACTIVY; - break; - - case BUDGET_KNC1C: - case BUDGET_KNC1CP: - case BUDGET_CIN1200C: - case BUDGET_KNC1C_MK3: - case BUDGET_KNC1C_TDA10024: - case BUDGET_KNC1CP_MK3: - case BUDGET_CIN1200C_MK3: - budget->buffer_width = TS_WIDTH_DVBC; - max_bufsize = TS_MAX_BUFSIZE_K_DVBC; - height_mask = TS_HEIGHT_MASK_DVBC; - break; - - default: - budget->buffer_width = TS_WIDTH; - max_bufsize = TS_MAX_BUFSIZE_K; - height_mask = TS_HEIGHT_MASK; - } - - if (dma_buffer_size < TS_MIN_BUFSIZE_K) - dma_buffer_size = TS_MIN_BUFSIZE_K; - else if (dma_buffer_size > max_bufsize) - dma_buffer_size = max_bufsize; - - budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; - if (budget->buffer_height > 0xfff) { - budget->buffer_height /= 2; - budget->buffer_height &= height_mask; - budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; - } else { - budget->buffer_height &= height_mask; - budget->buffer_size = budget->buffer_height * budget->buffer_width; - } - budget->buffer_warning_threshold = budget->buffer_size * 80/100; - budget->buffer_warnings = 0; - budget->buffer_warning_time = jiffies; - - dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", - budget->dev->name, - budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", - budget->buffer_width, budget->buffer_height); - printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); - - ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, - owner, &budget->dev->pci->dev, adapter_nums); - if (ret < 0) - return ret; - - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25)); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - saa7146_write(dev, DD1_INIT, 0x02000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - if (bi->type != BUDGET_FS_ACTIVY) - budget->video_port = BUDGET_VIDEO_PORTB; - else - budget->video_port = BUDGET_VIDEO_PORTA; - spin_lock_init(&budget->feedlock); - spin_lock_init(&budget->debilock); - - /* the Siemens DVB needs this if you want to have the i2c chips - get recognized before the main driver is loaded */ - if (bi->type != BUDGET_FS_ACTIVY) - saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ - - strscpy(budget->i2c_adap.name, budget->card->name, - sizeof(budget->i2c_adap.name)); - - saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); - strscpy(budget->i2c_adap.name, budget->card->name, - sizeof(budget->i2c_adap.name)); - - if (i2c_add_adapter(&budget->i2c_adap) < 0) { - ret = -ENOMEM; - goto err_dvb_unregister; - } - - ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); - - budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); - if (NULL == budget->grabbing) { - ret = -ENOMEM; - goto err_del_i2c; - } - - saa7146_write(dev, PCI_BT_V1, 0x001c0000); - /* upload all */ - saa7146_write(dev, GPIO_CTRL, 0x000000); - - tasklet_setup(&budget->vpe_tasklet, vpeirq); - - /* frontend power on */ - if (bi->type != BUDGET_FS_ACTIVY) - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - - if ((ret = budget_register(budget)) == 0) - return 0; /* Everything OK */ - - /* An error occurred, cleanup resources */ - saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); - -err_del_i2c: - i2c_del_adapter(&budget->i2c_adap); - -err_dvb_unregister: - dvb_unregister_adapter(&budget->dvb_adapter); - - return ret; -} - -void ttpci_budget_init_hooks(struct budget *budget) -{ - if (budget->dvb_frontend && !budget->read_fe_status) { - budget->read_fe_status = budget->dvb_frontend->ops.read_status; - budget->dvb_frontend->ops.read_status = budget_read_fe_status; - } -} - -int ttpci_budget_deinit(struct budget *budget) -{ - struct saa7146_dev *dev = budget->dev; - - dprintk(2, "budget: %p\n", budget); - - budget_unregister(budget); - - tasklet_kill(&budget->vpe_tasklet); - - saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); - - i2c_del_adapter(&budget->i2c_adap); - - dvb_unregister_adapter(&budget->dvb_adapter); - - return 0; -} - -void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) -{ - struct budget *budget = (struct budget *) dev->ext_priv; - - dprintk(8, "dev: %p, budget: %p\n", dev, budget); - - if (*isr & MASK_10) - tasklet_schedule(&budget->vpe_tasklet); -} - -void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) -{ - struct budget *budget = (struct budget *) dev->ext_priv; - - spin_lock(&budget->feedlock); - budget->video_port = video_port; - if (budget->feeding) { - stop_ts_capture(budget); - start_ts_capture(budget); - } - spin_unlock(&budget->feedlock); -} - -EXPORT_SYMBOL_GPL(ttpci_budget_debiread); -EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); -EXPORT_SYMBOL_GPL(ttpci_budget_init); -EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); -EXPORT_SYMBOL_GPL(ttpci_budget_deinit); -EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); -EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); -EXPORT_SYMBOL_GPL(budget_debug); - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c deleted file mode 100644 index a88711a3ac7f..000000000000 --- a/drivers/media/pci/ttpci/budget.c +++ /dev/null @@ -1,883 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget.c: driver for the SAA7146 based Budget DVB cards - * - * Compiled from various sources by Michael Hunold - * - * Copyright (C) 2002 Ralph Metzler - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * 26feb2004 Support for FS Activy Card (Grundig tuner) by - * Michael Dreher , - * Oliver Endriss and - * Andreas 'randy' Weinberger - * - * the project's page is at https://linuxtv.org - */ - -#include "budget.h" -#include "stv0299.h" -#include "ves1x93.h" -#include "ves1820.h" -#include "l64781.h" -#include "tda8083.h" -#include "s5h1420.h" -#include "tda10086.h" -#include "tda826x.h" -#include "lnbp21.h" -#include "bsru6.h" -#include "bsbe1.h" -#include "tdhd1.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "isl6423.h" -#include "lnbh24.h" - - -static int diseqc_method; -module_param(diseqc_method, int, 0444); -MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static void Set22K (struct budget *budget, int state) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); -} - -/* Diseqc functions only for TT Budget card */ -/* taken from the Skyvision DVB driver by - Ralph Metzler */ - -static void DiseqcSendBit (struct budget *budget, int data) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(data ? 500 : 1000); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - udelay(data ? 1000 : 500); -} - -static void DiseqcSendByte (struct budget *budget, int data) -{ - int i, par=1, d; - - dprintk(2, "budget: %p\n", budget); - - for (i=7; i>=0; i--) { - d = (data>>i)&1; - par ^= d; - DiseqcSendBit(budget, d); - } - - DiseqcSendBit(budget, par); -} - -static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) -{ - struct saa7146_dev *dev=budget->dev; - int i; - - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - mdelay(16); - - for (i=0; idev; - - dprintk(2, "budget: %p\n", budget); - - switch (voltage) { - case SEC_VOLTAGE_13: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); - break; - case SEC_VOLTAGE_18: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); - saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); - break; - case SEC_VOLTAGE_OFF: - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int siemens_budget_set_voltage(struct dvb_frontend *fe, - enum fe_sec_voltage voltage) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - return SetVoltage_Activy (budget, voltage); -} - -static int budget_set_tone(struct dvb_frontend *fe, - enum fe_sec_tone_mode tone) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - switch (tone) { - case SEC_TONE_ON: - Set22K (budget, 1); - break; - - case SEC_TONE_OFF: - Set22K (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, 0, NULL, minicmd); - - return 0; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (c->frequency + 479500) / 125; - - if (c->frequency > 2000000) - pwr = 3; - else if (c->frequency > 1800000) - pwr = 2; - else if (c->frequency > 1600000) - pwr = 1; - else if (c->frequency > 1200000) - pwr = 0; - else if (c->frequency >= 1100000) - pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = -{ - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (c->frequency + 35937500 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85 | ((div >> 10) & 0x60); - data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct ves1820_config alps_tdbe2_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - -static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = fe->dvb->priv; - u8 *tuner_addr = fe->tuner_priv; - u32 div; - u8 cfg, cpump, band_select; - u8 data[4]; - struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; - - if (tuner_addr) - msg.addr = *tuner_addr; - else - msg.addr = 0x61; - - div = (36125000 + c->frequency) / 166666; - - cfg = 0x88; - - if (c->frequency < 175000000) - cpump = 2; - else if (c->frequency < 390000000) - cpump = 1; - else if (c->frequency < 470000000) - cpump = 2; - else if (c->frequency < 750000000) - cpump = 1; - else - cpump = 3; - - if (c->frequency < 175000000) - band_select = 0x0e; - else if (c->frequency < 470000000) - band_select = 0x05; - else - band_select = 0x03; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = ((div >> 10) & 0x60) | cfg; - data[3] = (cpump << 6) | band_select; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct l64781_config grundig_29504_401_config = { - .demod_address = 0x55, -}; - -static struct l64781_config grundig_29504_401_config_activy = { - .demod_address = 0x54, -}; - -static u8 tuner_address_grundig_29504_401_activy = 0x60; - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = c->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - -static int s5h1420_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = c->frequency / 1000; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xc2; - - if (div < 1450) - data[3] = 0x00; - else if (div < 1850) - data[3] = 0x40; - else if (div < 2000) - data[3] = 0x80; - else - data[3] = 0xc0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - - return 0; -} - -static struct s5h1420_config s5h1420_config = { - .demod_address = 0x53, - .invert = 1, - .cdclk_polarity = 1, -}; - -static struct tda10086_config tda10086_config = { - .demod_address = 0x0e, - .invert = 0, - .diseqc_tone = 1, - .xtal_freq = TDA10086_XTAL_16M, -}; - -static const struct stv0299_config alps_bsru6_config_activy = { - .demod_address = 0x68, - .inittab = alps_bsru6_inittab, - .mclk = 88000000UL, - .invert = 1, - .op0_off = 1, - .min_delay_ms = 100, - .set_symbol_rate = alps_bsru6_set_symbol_rate, -}; - -static const struct stv0299_config alps_bsbe1_config_activy = { - .demod_address = 0x68, - .inittab = alps_bsbe1_inittab, - .mclk = 88000000UL, - .invert = 1, - .op0_off = 1, - .min_delay_ms = 100, - .set_symbol_rate = alps_bsbe1_set_symbol_rate, -}; - -static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) -{ - struct budget *budget = (struct budget *)fe->dvb->priv; - - return request_firmware(fw, name, &budget->dev->pci->dev); -} - - -static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) -{ - u8 val; - struct i2c_msg msg[] = { - { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } - }; - - return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; -} - -static u8 read_pwm(struct budget* budget) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, - { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; - - if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static struct stv090x_config tt1600_stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 13500000, - .address = 0x68, - - .ts1_mode = STV090x_TSMODE_DVBCI, - .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - - .repeater_level = STV090x_RPTLEVEL_16, - - .tuner_init = NULL, - .tuner_sleep = NULL, - .tuner_set_mode = NULL, - .tuner_set_frequency = NULL, - .tuner_get_frequency = NULL, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = NULL, - .tuner_set_bbgain = NULL, - .tuner_get_bbgain = NULL, - .tuner_set_refclk = NULL, - .tuner_get_status = NULL, -}; - -static struct stv6110x_config tt1600_stv6110x_config = { - .addr = 0x60, - .refclk = 27000000, - .clk_div = 2, -}; - -static struct isl6423_config tt1600_isl6423_config = { - .current_max = SEC_CURRENT_515m, - .curlim = SEC_CURRENT_LIM_ON, - .mod_extern = 1, - .addr = 0x08, -}; - -static void frontend_init(struct budget *budget) -{ - (void)alps_bsbe1_config; /* avoid warning */ - - switch(budget->dev->pci->subsystem_device) { - case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) - case 0x1013: - // try the ALPS BSRV2 first of all - budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - - // try the ALPS BSRU6 now - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - } - break; - } - break; - - case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) - - budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - break; - } - break; - - case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) - - budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - budget->dvb_frontend->tuner_priv = NULL; - break; - } - break; - - case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */ - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n"); - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - break; - } - break; - - case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ - { - int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); - - if (subtype < 0) - break; - /* fixme: find a better way to identify the card */ - if (subtype < 0x36) { - /* assume ALPS BSRU6 */ - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - break; - } - } else { - /* assume ALPS BSBE1 */ - /* reset tuner */ - saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); - msleep(250); - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - break; - } - } - break; - } - - case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) - budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; - } - break; - - case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ - budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - } - break; - - case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ - budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - } - break; - - case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) - { - struct dvb_frontend *fe; - - fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params; - budget->dvb_frontend = fe; - if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap, - 0, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - goto error_out; - } - break; - } - } - fallthrough; - case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) - { - struct dvb_frontend *fe; - - // gpio2 is connected to CLB - reset it + leave it high - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(1); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(1); - - fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); - if (fe) { - budget->dvb_frontend = fe; - if (dvb_attach(tda826x_attach, fe, 0x60, - &budget->i2c_adap, 0) == NULL) - printk("%s: No tda826x found!\n", __func__); - if (dvb_attach(lnbp21_attach, fe, - &budget->i2c_adap, 0, 0) == NULL) { - printk("%s: No LNBP21 found!\n", __func__); - goto error_out; - } - break; - } - } - fallthrough; - - case 0x101c: { /* TT S2-1600 */ - const struct stv6110x_devctl *ctl; - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(250); - - budget->dvb_frontend = dvb_attach(stv090x_attach, - &tt1600_stv090x_config, - &budget->i2c_adap, - STV090x_DEMODULATOR_0); - - if (budget->dvb_frontend) { - - ctl = dvb_attach(stv6110x_attach, - budget->dvb_frontend, - &tt1600_stv6110x_config, - &budget->i2c_adap); - - if (ctl) { - tt1600_stv090x_config.tuner_init = ctl->tuner_init; - tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; - tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; - tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; - tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; - tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; - tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; - tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; - tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; - tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; - tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; - - /* call the init function once to initialize - tuner's clock output divider and demod's - master clock */ - if (budget->dvb_frontend->ops.init) - budget->dvb_frontend->ops.init(budget->dvb_frontend); - - if (dvb_attach(isl6423_attach, - budget->dvb_frontend, - &budget->i2c_adap, - &tt1600_isl6423_config) == NULL) { - printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); - goto error_out; - } - } else { - printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); - goto error_out; - } - } - } - break; - - case 0x1020: { /* Omicom S2 */ - const struct stv6110x_devctl *ctl; - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); - msleep(50); - saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); - msleep(250); - - budget->dvb_frontend = dvb_attach(stv090x_attach, - &tt1600_stv090x_config, - &budget->i2c_adap, - STV090x_DEMODULATOR_0); - - if (budget->dvb_frontend) { - printk(KERN_INFO "budget: Omicom S2 detected\n"); - - ctl = dvb_attach(stv6110x_attach, - budget->dvb_frontend, - &tt1600_stv6110x_config, - &budget->i2c_adap); - - if (ctl) { - tt1600_stv090x_config.tuner_init = ctl->tuner_init; - tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; - tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; - tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; - tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; - tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; - tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; - tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; - tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; - tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; - tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; - - /* call the init function once to initialize - tuner's clock output divider and demod's - master clock */ - if (budget->dvb_frontend->ops.init) - budget->dvb_frontend->ops.init(budget->dvb_frontend); - - if (dvb_attach(lnbh24_attach, - budget->dvb_frontend, - &budget->i2c_adap, - LNBH24_PCL | LNBH24_TTX, - LNBH24_TEN, 0x14>>1) == NULL) { - printk(KERN_ERR - "No LNBH24 found!\n"); - goto error_out; - } - } else { - printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); - goto error_out; - } - } - } - break; - } - - if (budget->dvb_frontend == NULL) { - printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget->dev->pci->vendor, - budget->dev->pci->device, - budget->dev->pci->subsystem_vendor, - budget->dev->pci->subsystem_device); - } else { - if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) - goto error_out; - } - return; - -error_out: - printk("budget: Frontend registration failed!\n"); - dvb_frontend_detach(budget->dvb_frontend); - budget->dvb_frontend = NULL; - return; -} - -static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct budget *budget = NULL; - int err; - - budget = kmalloc(sizeof(struct budget), GFP_KERNEL); - if( NULL == budget ) { - return -ENOMEM; - } - - dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); - - dev->ext_priv = budget; - - err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); - if (err) { - printk("==> failed\n"); - kfree (budget); - return err; - } - - budget->dvb_adapter.priv = budget; - frontend_init(budget); - - ttpci_budget_init_hooks(budget); - - return 0; -} - -static int budget_detach (struct saa7146_dev* dev) -{ - struct budget *budget = (struct budget*) dev->ext_priv; - int err; - - if (budget->dvb_frontend) { - dvb_unregister_frontend(budget->dvb_frontend); - dvb_frontend_detach(budget->dvb_frontend); - } - - err = ttpci_budget_deinit (budget); - - kfree (budget); - dev->ext_priv = NULL; - - return err; -} - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); -MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); -MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); -MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); -MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); -MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), - MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), - MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), - MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), - MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), - MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), - MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), - MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), - MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), - MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), - MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), - MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), - MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52), - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_extension budget_extension = { - .name = "budget dvb", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = pci_tbl, - .attach = budget_attach, - .detach = budget_detach, - - .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, -}; - -static int __init budget_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -module_init(budget_init); -module_exit(budget_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); -MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h deleted file mode 100644 index bd87432e6cde..000000000000 --- a/drivers/media/pci/ttpci/budget.h +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __BUDGET_DVB__ -#define __BUDGET_DVB__ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -extern int budget_debug; - -#ifdef dprintk -#undef dprintk -#endif - -#define dprintk(level, fmt, arg...) do { \ - if (level & budget_debug) \ - printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ - __func__, ##arg); \ -} while (0) - -#define TS_SIZE 188 - -struct budget_info { - char *name; - int type; -}; - -/* place to store all the necessary device information */ -struct budget { - - /* devices */ - struct dvb_device dvb_dev; - struct dvb_net dvb_net; - - struct saa7146_dev *dev; - - struct i2c_adapter i2c_adap; - struct budget_info *card; - - unsigned char *grabbing; - struct saa7146_pgtable pt; - - struct tasklet_struct fidb_tasklet; - struct tasklet_struct vpe_tasklet; - - struct dmxdev dmxdev; - struct dvb_demux demux; - - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - - int ci_present; - int video_port; - - u32 buffer_width; - u32 buffer_height; - u32 buffer_size; - u32 buffer_warning_threshold; - u32 buffer_warnings; - unsigned long buffer_warning_time; - - u32 ttbp; - int feeding; - - spinlock_t feedlock; - - spinlock_t debilock; - - struct dvb_adapter dvb_adapter; - struct dvb_frontend *dvb_frontend; - int (*read_fe_status)(struct dvb_frontend *fe, enum fe_status *status); - int fe_synced; - - void *priv; -}; - -#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ -static struct budget_info x_var ## _info = { \ - .name=x_name, \ - .type=x_type }; \ -static struct saa7146_pci_extension_data x_var = { \ - .ext_priv = &x_var ## _info, \ - .ext = &budget_extension }; - -#define BUDGET_TT 0 -#define BUDGET_TT_HW_DISEQC 1 -#define BUDGET_PATCH 3 -#define BUDGET_FS_ACTIVY 4 -#define BUDGET_CIN1200S 5 -#define BUDGET_CIN1200C 6 -#define BUDGET_CIN1200T 7 -#define BUDGET_KNC1S 8 -#define BUDGET_KNC1C 9 -#define BUDGET_KNC1T 10 -#define BUDGET_KNC1SP 11 -#define BUDGET_KNC1CP 12 -#define BUDGET_KNC1TP 13 -#define BUDGET_TVSTAR 14 -#define BUDGET_CIN1200C_MK3 15 -#define BUDGET_KNC1C_MK3 16 -#define BUDGET_KNC1CP_MK3 17 -#define BUDGET_KNC1S2 18 -#define BUDGET_KNC1C_TDA10024 19 - -#define BUDGET_VIDEO_PORTA 0 -#define BUDGET_VIDEO_PORTB 1 - -extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, - struct saa7146_pci_extension_data *info, - struct module *owner, short *adapter_nums); -extern void ttpci_budget_init_hooks(struct budget *budget); -extern int ttpci_budget_deinit(struct budget *budget); -extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); -extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); -extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop); -extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, - int uselocks, int nobusyloop); - -#endif diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index e520241e7723..d78b8c3f0814 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -58,6 +58,7 @@ if STAGING_MEDIA_DEPRECATED source "drivers/staging/media/deprecated/cpia2/Kconfig" source "drivers/staging/media/deprecated/fsl-viu/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" +source "drivers/staging/media/deprecated/saa7146/Kconfig" source "drivers/staging/media/deprecated/stkwebcam/Kconfig" source "drivers/staging/media/deprecated/tm6000/Kconfig" source "drivers/staging/media/deprecated/vpfe_capture/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index ad2893d34cb8..3aa5e77b62f8 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ obj-$(CONFIG_DVB_AV7110) += av7110/ obj-y += deprecated/vpfe_capture/ +obj-y += deprecated/saa7146/ diff --git a/drivers/staging/media/av7110/Makefile b/drivers/staging/media/av7110/Makefile index 307b267598ea..c04cd0a59109 100644 --- a/drivers/staging/media/av7110/Makefile +++ b/drivers/staging/media/av7110/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_DVB_SP8870) += sp8870.o ccflags-y += -I $(srctree)/drivers/media/dvb-frontends ccflags-y += -I $(srctree)/drivers/media/tuners -ccflags-y += -I $(srctree)/drivers/media/pci/ttpci ccflags-y += -I $(srctree)/drivers/media/common +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/ttpci +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/common diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 809d938ae166..9fde69b38f1c 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -33,7 +33,7 @@ #include "stv0297.h" #include "l64781.h" -#include +#include "saa7146_vv.h" #define ANALOG_TUNER_VES1820 1 diff --git a/drivers/staging/media/deprecated/saa7146/Kconfig b/drivers/staging/media/deprecated/saa7146/Kconfig new file mode 100644 index 000000000000..d0cb52164ff8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +source "drivers/staging/media/deprecated/saa7146/common/Kconfig" +source "drivers/staging/media/deprecated/saa7146/saa7146/Kconfig" +source "drivers/staging/media/deprecated/saa7146/ttpci/Kconfig" diff --git a/drivers/staging/media/deprecated/saa7146/Makefile b/drivers/staging/media/deprecated/saa7146/Makefile new file mode 100644 index 000000000000..9d99fdedf813 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/Makefile @@ -0,0 +1,2 @@ + # SPDX-License-Identifier: GPL-2.0-only +obj-y += common/ saa7146/ ttpci/ diff --git a/drivers/staging/media/deprecated/saa7146/common/Kconfig b/drivers/staging/media/deprecated/saa7146/common/Kconfig new file mode 100644 index 000000000000..a0aa155e5d85 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SAA7146 + tristate + depends on I2C && PCI + +config VIDEO_SAA7146_VV + tristate + depends on VIDEO_DEV + select VIDEOBUF_DMA_SG + select VIDEO_SAA7146 diff --git a/drivers/staging/media/deprecated/saa7146/common/Makefile b/drivers/staging/media/deprecated/saa7146/common/Makefile new file mode 100644 index 000000000000..2a6337feaec8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +saa7146-objs := saa7146_i2c.o saa7146_core.o +saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o + +obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o +obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146.h b/drivers/staging/media/deprecated/saa7146/common/saa7146.h new file mode 100644 index 000000000000..71ce63c99cb4 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146.h @@ -0,0 +1,472 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SAA7146__ +#define __SAA7146__ + +#include /* for delay-stuff */ +#include /* for kmalloc/kfree */ +#include /* for pci-config-stuff, vendor ids etc. */ +#include /* for "__init" */ +#include /* for IMMEDIATE_BH */ +#include /* for kernel module loader */ +#include /* for i2c subsystem */ +#include /* for accessing devices */ +#include +#include +#include +#include +#include + +#include /* for vmalloc() */ +#include /* for vmalloc_to_page() */ + +#define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr))) +#define saa7146_read(sxy,adr) readl(sxy->mem+(adr)) + +extern unsigned int saa7146_debug; + +#ifndef DEBUG_VARIABLE + #define DEBUG_VARIABLE saa7146_debug +#endif + +#define ERR(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) + +#define _DBG(mask, fmt, ...) \ +do { \ + if (DEBUG_VARIABLE & mask) \ + pr_debug("%s(): " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +/* simple debug messages */ +#define DEB_S(fmt, ...) _DBG(0x01, fmt, ##__VA_ARGS__) +/* more detailed debug messages */ +#define DEB_D(fmt, ...) _DBG(0x02, fmt, ##__VA_ARGS__) +/* print enter and exit of functions */ +#define DEB_EE(fmt, ...) _DBG(0x04, fmt, ##__VA_ARGS__) +/* i2c debug messages */ +#define DEB_I2C(fmt, ...) _DBG(0x08, fmt, ##__VA_ARGS__) +/* vbi debug messages */ +#define DEB_VBI(fmt, ...) _DBG(0x10, fmt, ##__VA_ARGS__) +/* interrupt debug messages */ +#define DEB_INT(fmt, ...) _DBG(0x20, fmt, ##__VA_ARGS__) +/* capture debug messages */ +#define DEB_CAP(fmt, ...) _DBG(0x40, fmt, ##__VA_ARGS__) + +#define SAA7146_ISR_CLEAR(x,y) \ + saa7146_write(x, ISR, (y)); + +struct module; + +struct saa7146_dev; +struct saa7146_extension; +struct saa7146_vv; + +/* saa7146 page table */ +struct saa7146_pgtable { + unsigned int size; + __le32 *cpu; + dma_addr_t dma; + /* used for offsets for u,v planes for planar capture modes */ + unsigned long offset; + /* used for custom pagetables (used for example by budget dvb cards) */ + struct scatterlist *slist; + int nents; +}; + +struct saa7146_pci_extension_data { + struct saa7146_extension *ext; + void *ext_priv; /* most likely a name string */ +}; + +#define MAKE_EXTENSION_PCI(x_var, x_vendor, x_device) \ + { \ + .vendor = PCI_VENDOR_ID_PHILIPS, \ + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, \ + .subvendor = x_vendor, \ + .subdevice = x_device, \ + .driver_data = (unsigned long)& x_var, \ + } + +struct saa7146_extension +{ + char name[32]; /* name of the device */ +#define SAA7146_USE_I2C_IRQ 0x1 +#define SAA7146_I2C_SHORT_DELAY 0x2 + int flags; + + /* pairs of subvendor and subdevice ids for + supported devices, last entry 0xffff, 0xfff */ + struct module *module; + struct pci_driver driver; + const struct pci_device_id *pci_tbl; + + /* extension functions */ + int (*probe)(struct saa7146_dev *); + int (*attach)(struct saa7146_dev *, struct saa7146_pci_extension_data *); + int (*detach)(struct saa7146_dev*); + + u32 irq_mask; /* mask to indicate, which irq-events are handled by the extension */ + void (*irq_func)(struct saa7146_dev*, u32* irq_mask); +}; + +struct saa7146_dma +{ + dma_addr_t dma_handle; + __le32 *cpu_addr; +}; + +struct saa7146_dev +{ + struct module *module; + + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + + /* different device locks */ + spinlock_t slock; + struct mutex v4l2_lock; + + unsigned char __iomem *mem; /* pointer to mapped IO memory */ + u32 revision; /* chip revision; needed for bug-workarounds*/ + + /* pci-device & irq stuff*/ + char name[32]; + struct pci_dev *pci; + u32 int_todo; + spinlock_t int_slock; + + /* extension handling */ + struct saa7146_extension *ext; /* indicates if handled by extension */ + void *ext_priv; /* pointer for extension private use (most likely some private data) */ + struct saa7146_ext_vv *ext_vv_data; + + /* per device video/vbi information (if available) */ + struct saa7146_vv *vv_data; + void (*vv_callback)(struct saa7146_dev *dev, unsigned long status); + + /* i2c-stuff */ + struct mutex i2c_lock; + + u32 i2c_bitrate; + struct saa7146_dma d_i2c; /* pointer to i2c memory */ + wait_queue_head_t i2c_wq; + int i2c_op; + + /* memories */ + struct saa7146_dma d_rps0; + struct saa7146_dma d_rps1; +}; + +static inline struct saa7146_dev *to_saa7146_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct saa7146_dev, v4l2_dev); +} + +/* from saa7146_i2c.c */ +int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate); + +/* from saa7146_core.c */ +int saa7146_register_extension(struct saa7146_extension*); +int saa7146_unregister_extension(struct saa7146_extension*); +struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc); +int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt); +void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt); +int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length ); +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt); +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt); +void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data); +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop); + +/* some memory sizes */ +#define SAA7146_I2C_MEM ( 1*PAGE_SIZE) +#define SAA7146_RPS_MEM ( 1*PAGE_SIZE) + +/* some i2c constants */ +#define SAA7146_I2C_TIMEOUT 100 /* i2c-timeout-value in ms */ +#define SAA7146_I2C_RETRIES 3 /* how many times shall we retry an i2c-operation? */ +#define SAA7146_I2C_DELAY 5 /* time we wait after certain i2c-operations */ + +/* unsorted defines */ +#define ME1 0x0000000800 +#define PV1 0x0000000008 + +/* gpio defines */ +#define SAA7146_GPIO_INPUT 0x00 +#define SAA7146_GPIO_IRQHI 0x10 +#define SAA7146_GPIO_IRQLO 0x20 +#define SAA7146_GPIO_IRQHL 0x30 +#define SAA7146_GPIO_OUTLO 0x40 +#define SAA7146_GPIO_OUTHI 0x50 + +/* debi defines */ +#define DEBINOSWAP 0x000e0000 + +/* define for the register programming sequencer (rps) */ +#define CMD_NOP 0x00000000 /* No operation */ +#define CMD_CLR_EVENT 0x00000000 /* Clear event */ +#define CMD_SET_EVENT 0x10000000 /* Set signal event */ +#define CMD_PAUSE 0x20000000 /* Pause */ +#define CMD_CHECK_LATE 0x30000000 /* Check late */ +#define CMD_UPLOAD 0x40000000 /* Upload */ +#define CMD_STOP 0x50000000 /* Stop */ +#define CMD_INTERRUPT 0x60000000 /* Interrupt */ +#define CMD_JUMP 0x80000000 /* Jump */ +#define CMD_WR_REG 0x90000000 /* Write (load) register */ +#define CMD_RD_REG 0xa0000000 /* Read (store) register */ +#define CMD_WR_REG_MASK 0xc0000000 /* Write register with mask */ + +#define CMD_OAN MASK_27 +#define CMD_INV MASK_26 +#define CMD_SIG4 MASK_25 +#define CMD_SIG3 MASK_24 +#define CMD_SIG2 MASK_23 +#define CMD_SIG1 MASK_22 +#define CMD_SIG0 MASK_21 +#define CMD_O_FID_B MASK_14 +#define CMD_E_FID_B MASK_13 +#define CMD_O_FID_A MASK_12 +#define CMD_E_FID_A MASK_11 + +/* some events and command modifiers for rps1 squarewave generator */ +#define EVT_HS (1<<15) // Source Line Threshold reached +#define EVT_VBI_B (1<<9) // VSYNC Event +#define RPS_OAN (1<<27) // 1: OR events, 0: AND events +#define RPS_INV (1<<26) // Invert (compound) event +#define GPIO3_MSK 0xFF000000 // GPIO #3 control bits + +/* Bit mask constants */ +#define MASK_00 0x00000001 /* Mask value for bit 0 */ +#define MASK_01 0x00000002 /* Mask value for bit 1 */ +#define MASK_02 0x00000004 /* Mask value for bit 2 */ +#define MASK_03 0x00000008 /* Mask value for bit 3 */ +#define MASK_04 0x00000010 /* Mask value for bit 4 */ +#define MASK_05 0x00000020 /* Mask value for bit 5 */ +#define MASK_06 0x00000040 /* Mask value for bit 6 */ +#define MASK_07 0x00000080 /* Mask value for bit 7 */ +#define MASK_08 0x00000100 /* Mask value for bit 8 */ +#define MASK_09 0x00000200 /* Mask value for bit 9 */ +#define MASK_10 0x00000400 /* Mask value for bit 10 */ +#define MASK_11 0x00000800 /* Mask value for bit 11 */ +#define MASK_12 0x00001000 /* Mask value for bit 12 */ +#define MASK_13 0x00002000 /* Mask value for bit 13 */ +#define MASK_14 0x00004000 /* Mask value for bit 14 */ +#define MASK_15 0x00008000 /* Mask value for bit 15 */ +#define MASK_16 0x00010000 /* Mask value for bit 16 */ +#define MASK_17 0x00020000 /* Mask value for bit 17 */ +#define MASK_18 0x00040000 /* Mask value for bit 18 */ +#define MASK_19 0x00080000 /* Mask value for bit 19 */ +#define MASK_20 0x00100000 /* Mask value for bit 20 */ +#define MASK_21 0x00200000 /* Mask value for bit 21 */ +#define MASK_22 0x00400000 /* Mask value for bit 22 */ +#define MASK_23 0x00800000 /* Mask value for bit 23 */ +#define MASK_24 0x01000000 /* Mask value for bit 24 */ +#define MASK_25 0x02000000 /* Mask value for bit 25 */ +#define MASK_26 0x04000000 /* Mask value for bit 26 */ +#define MASK_27 0x08000000 /* Mask value for bit 27 */ +#define MASK_28 0x10000000 /* Mask value for bit 28 */ +#define MASK_29 0x20000000 /* Mask value for bit 29 */ +#define MASK_30 0x40000000 /* Mask value for bit 30 */ +#define MASK_31 0x80000000 /* Mask value for bit 31 */ + +#define MASK_B0 0x000000ff /* Mask value for byte 0 */ +#define MASK_B1 0x0000ff00 /* Mask value for byte 1 */ +#define MASK_B2 0x00ff0000 /* Mask value for byte 2 */ +#define MASK_B3 0xff000000 /* Mask value for byte 3 */ + +#define MASK_W0 0x0000ffff /* Mask value for word 0 */ +#define MASK_W1 0xffff0000 /* Mask value for word 1 */ + +#define MASK_PA 0xfffffffc /* Mask value for physical address */ +#define MASK_PR 0xfffffffe /* Mask value for protection register */ +#define MASK_ER 0xffffffff /* Mask value for the entire register */ + +#define MASK_NONE 0x00000000 /* No mask */ + +/* register aliases */ +#define BASE_ODD1 0x00 /* Video DMA 1 registers */ +#define BASE_EVEN1 0x04 +#define PROT_ADDR1 0x08 +#define PITCH1 0x0C +#define BASE_PAGE1 0x10 /* Video DMA 1 base page */ +#define NUM_LINE_BYTE1 0x14 + +#define BASE_ODD2 0x18 /* Video DMA 2 registers */ +#define BASE_EVEN2 0x1C +#define PROT_ADDR2 0x20 +#define PITCH2 0x24 +#define BASE_PAGE2 0x28 /* Video DMA 2 base page */ +#define NUM_LINE_BYTE2 0x2C + +#define BASE_ODD3 0x30 /* Video DMA 3 registers */ +#define BASE_EVEN3 0x34 +#define PROT_ADDR3 0x38 +#define PITCH3 0x3C +#define BASE_PAGE3 0x40 /* Video DMA 3 base page */ +#define NUM_LINE_BYTE3 0x44 + +#define PCI_BT_V1 0x48 /* Video/FIFO 1 */ +#define PCI_BT_V2 0x49 /* Video/FIFO 2 */ +#define PCI_BT_V3 0x4A /* Video/FIFO 3 */ +#define PCI_BT_DEBI 0x4B /* DEBI */ +#define PCI_BT_A 0x4C /* Audio */ + +#define DD1_INIT 0x50 /* Init setting of DD1 interface */ + +#define DD1_STREAM_B 0x54 /* DD1 B video data stream handling */ +#define DD1_STREAM_A 0x56 /* DD1 A video data stream handling */ + +#define BRS_CTRL 0x58 /* BRS control register */ +#define HPS_CTRL 0x5C /* HPS control register */ +#define HPS_V_SCALE 0x60 /* HPS vertical scale */ +#define HPS_V_GAIN 0x64 /* HPS vertical ACL and gain */ +#define HPS_H_PRESCALE 0x68 /* HPS horizontal prescale */ +#define HPS_H_SCALE 0x6C /* HPS horizontal scale */ +#define BCS_CTRL 0x70 /* BCS control */ +#define CHROMA_KEY_RANGE 0x74 +#define CLIP_FORMAT_CTRL 0x78 /* HPS outputs formats & clipping */ + +#define DEBI_CONFIG 0x7C +#define DEBI_COMMAND 0x80 +#define DEBI_PAGE 0x84 +#define DEBI_AD 0x88 + +#define I2C_TRANSFER 0x8C +#define I2C_STATUS 0x90 + +#define BASE_A1_IN 0x94 /* Audio 1 input DMA */ +#define PROT_A1_IN 0x98 +#define PAGE_A1_IN 0x9C + +#define BASE_A1_OUT 0xA0 /* Audio 1 output DMA */ +#define PROT_A1_OUT 0xA4 +#define PAGE_A1_OUT 0xA8 + +#define BASE_A2_IN 0xAC /* Audio 2 input DMA */ +#define PROT_A2_IN 0xB0 +#define PAGE_A2_IN 0xB4 + +#define BASE_A2_OUT 0xB8 /* Audio 2 output DMA */ +#define PROT_A2_OUT 0xBC +#define PAGE_A2_OUT 0xC0 + +#define RPS_PAGE0 0xC4 /* RPS task 0 page register */ +#define RPS_PAGE1 0xC8 /* RPS task 1 page register */ + +#define RPS_THRESH0 0xCC /* HBI threshold for task 0 */ +#define RPS_THRESH1 0xD0 /* HBI threshold for task 1 */ + +#define RPS_TOV0 0xD4 /* RPS timeout for task 0 */ +#define RPS_TOV1 0xD8 /* RPS timeout for task 1 */ + +#define IER 0xDC /* Interrupt enable register */ + +#define GPIO_CTRL 0xE0 /* GPIO 0-3 register */ + +#define EC1SSR 0xE4 /* Event cnt set 1 source select */ +#define EC2SSR 0xE8 /* Event cnt set 2 source select */ +#define ECT1R 0xEC /* Event cnt set 1 thresholds */ +#define ECT2R 0xF0 /* Event cnt set 2 thresholds */ + +#define ACON1 0xF4 +#define ACON2 0xF8 + +#define MC1 0xFC /* Main control register 1 */ +#define MC2 0x100 /* Main control register 2 */ + +#define RPS_ADDR0 0x104 /* RPS task 0 address register */ +#define RPS_ADDR1 0x108 /* RPS task 1 address register */ + +#define ISR 0x10C /* Interrupt status register */ +#define PSR 0x110 /* Primary status register */ +#define SSR 0x114 /* Secondary status register */ + +#define EC1R 0x118 /* Event counter set 1 register */ +#define EC2R 0x11C /* Event counter set 2 register */ + +#define PCI_VDP1 0x120 /* Video DMA pointer of FIFO 1 */ +#define PCI_VDP2 0x124 /* Video DMA pointer of FIFO 2 */ +#define PCI_VDP3 0x128 /* Video DMA pointer of FIFO 3 */ +#define PCI_ADP1 0x12C /* Audio DMA pointer of audio out 1 */ +#define PCI_ADP2 0x130 /* Audio DMA pointer of audio in 1 */ +#define PCI_ADP3 0x134 /* Audio DMA pointer of audio out 2 */ +#define PCI_ADP4 0x138 /* Audio DMA pointer of audio in 2 */ +#define PCI_DMA_DDP 0x13C /* DEBI DMA pointer */ + +#define LEVEL_REP 0x140, +#define A_TIME_SLOT1 0x180, /* from 180 - 1BC */ +#define A_TIME_SLOT2 0x1C0, /* from 1C0 - 1FC */ + +/* isr masks */ +#define SPCI_PPEF 0x80000000 /* PCI parity error */ +#define SPCI_PABO 0x40000000 /* PCI access error (target or master abort) */ +#define SPCI_PPED 0x20000000 /* PCI parity error on 'real time data' */ +#define SPCI_RPS_I1 0x10000000 /* Interrupt issued by RPS1 */ +#define SPCI_RPS_I0 0x08000000 /* Interrupt issued by RPS0 */ +#define SPCI_RPS_LATE1 0x04000000 /* RPS task 1 is late */ +#define SPCI_RPS_LATE0 0x02000000 /* RPS task 0 is late */ +#define SPCI_RPS_E1 0x01000000 /* RPS error from task 1 */ +#define SPCI_RPS_E0 0x00800000 /* RPS error from task 0 */ +#define SPCI_RPS_TO1 0x00400000 /* RPS timeout task 1 */ +#define SPCI_RPS_TO0 0x00200000 /* RPS timeout task 0 */ +#define SPCI_UPLD 0x00100000 /* RPS in upload */ +#define SPCI_DEBI_S 0x00080000 /* DEBI status */ +#define SPCI_DEBI_E 0x00040000 /* DEBI error */ +#define SPCI_IIC_S 0x00020000 /* I2C status */ +#define SPCI_IIC_E 0x00010000 /* I2C error */ +#define SPCI_A2_IN 0x00008000 /* Audio 2 input DMA protection / limit */ +#define SPCI_A2_OUT 0x00004000 /* Audio 2 output DMA protection / limit */ +#define SPCI_A1_IN 0x00002000 /* Audio 1 input DMA protection / limit */ +#define SPCI_A1_OUT 0x00001000 /* Audio 1 output DMA protection / limit */ +#define SPCI_AFOU 0x00000800 /* Audio FIFO over- / underflow */ +#define SPCI_V_PE 0x00000400 /* Video protection address */ +#define SPCI_VFOU 0x00000200 /* Video FIFO over- / underflow */ +#define SPCI_FIDA 0x00000100 /* Field ID video port A */ +#define SPCI_FIDB 0x00000080 /* Field ID video port B */ +#define SPCI_PIN3 0x00000040 /* GPIO pin 3 */ +#define SPCI_PIN2 0x00000020 /* GPIO pin 2 */ +#define SPCI_PIN1 0x00000010 /* GPIO pin 1 */ +#define SPCI_PIN0 0x00000008 /* GPIO pin 0 */ +#define SPCI_ECS 0x00000004 /* Event counter 1, 2, 4, 5 */ +#define SPCI_EC3S 0x00000002 /* Event counter 3 */ +#define SPCI_EC0S 0x00000001 /* Event counter 0 */ + +/* i2c */ +#define SAA7146_I2C_ABORT (1<<7) +#define SAA7146_I2C_SPERR (1<<6) +#define SAA7146_I2C_APERR (1<<5) +#define SAA7146_I2C_DTERR (1<<4) +#define SAA7146_I2C_DRERR (1<<3) +#define SAA7146_I2C_AL (1<<2) +#define SAA7146_I2C_ERR (1<<1) +#define SAA7146_I2C_BUSY (1<<0) + +#define SAA7146_I2C_START (0x3) +#define SAA7146_I2C_CONT (0x2) +#define SAA7146_I2C_STOP (0x1) +#define SAA7146_I2C_NOP (0x0) + +#define SAA7146_I2C_BUS_BIT_RATE_6400 (0x500) +#define SAA7146_I2C_BUS_BIT_RATE_3200 (0x100) +#define SAA7146_I2C_BUS_BIT_RATE_480 (0x400) +#define SAA7146_I2C_BUS_BIT_RATE_320 (0x600) +#define SAA7146_I2C_BUS_BIT_RATE_240 (0x700) +#define SAA7146_I2C_BUS_BIT_RATE_120 (0x000) +#define SAA7146_I2C_BUS_BIT_RATE_80 (0x200) +#define SAA7146_I2C_BUS_BIT_RATE_60 (0x300) + +static inline void SAA7146_IER_DISABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) & ~y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + +static inline void SAA7146_IER_ENABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) | y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c new file mode 100644 index 000000000000..da21d346b870 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + saa7146.o - driver for generic saa7146-based hardware + + Copyright (C) 1998-2003 Michael Hunold + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "saa7146.h" + +static int saa7146_num; + +unsigned int saa7146_debug; + +module_param(saa7146_debug, uint, 0644); +MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); + +#if 0 +static void dump_registers(struct saa7146_dev* dev) +{ + int i = 0; + + pr_info(" @ %li jiffies:\n", jiffies); + for (i = 0; i <= 0x148; i += 4) + pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i)); +} +#endif + +/**************************************************************************** + * gpio and debi helper functions + ****************************************************************************/ + +void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) +{ + u32 value = 0; + + BUG_ON(port > 3); + + value = saa7146_read(dev, GPIO_CTRL); + value &= ~(0xff << (8*port)); + value |= (data << (8*port)); + saa7146_write(dev, GPIO_CTRL, value); +} + +/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ +static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long timeout; + int err; + + /* wait for registers to be programmed */ + timeout = jiffies + usecs_to_jiffies(us1); + while (1) { + err = time_after(jiffies, timeout); + if (saa7146_read(dev, MC2) & 2) + break; + if (err) { + pr_debug("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + /* wait for transfer to complete */ + timeout = jiffies + usecs_to_jiffies(us2); + while (1) { + err = time_after(jiffies, timeout); + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (err) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long loops; + + /* wait for registers to be programmed */ + loops = us1; + while (1) { + if (saa7146_read(dev, MC2) & 2) + break; + if (!loops--) { + pr_err("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(1); + } + + /* wait for transfer to complete */ + loops = us2 / 5; + while (1) { + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (!loops--) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(5); + } + + return 0; +} + +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) +{ + if (nobusyloop) + return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000); + else + return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000); +} + +/**************************************************************************** + * general helper functions + ****************************************************************************/ + +/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c + make sure virt has been allocated with vmalloc_32(), otherwise the BUG() + may be triggered on highmem machines */ +static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) +{ + struct scatterlist *sglist; + struct page *pg; + int i; + + sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); + if (NULL == sglist) + return NULL; + sg_init_table(sglist, nr_pages); + for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { + pg = vmalloc_to_page(virt); + if (NULL == pg) + goto err; + BUG_ON(PageHighMem(pg)); + sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); + } + return sglist; + + err: + kfree(sglist); + return NULL; +} + +/********************************************************************************/ +/* common page table functions */ + +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) +{ + int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; + void *mem = vmalloc_32(length); + int slen = 0; + + if (NULL == mem) + goto err_null; + + if (!(pt->slist = vmalloc_to_sg(mem, pages))) + goto err_free_mem; + + if (saa7146_pgtable_alloc(pci, pt)) + goto err_free_slist; + + pt->nents = pages; + slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); + if (0 == slen) + goto err_free_pgtable; + + if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) + goto err_unmap_sg; + + return mem; + +err_unmap_sg: + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); +err_free_pgtable: + saa7146_pgtable_free(pci, pt); +err_free_slist: + kfree(pt->slist); + pt->slist = NULL; +err_free_mem: + vfree(mem); +err_null: + return NULL; +} + +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt) +{ + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); + saa7146_pgtable_free(pci, pt); + kfree(pt->slist); + pt->slist = NULL; + vfree(mem); +} + +void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) +{ + if (NULL == pt->cpu) + return; + dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma); + pt->cpu = NULL; +} + +int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) +{ + __le32 *cpu; + dma_addr_t dma_addr = 0; + + cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL); + if (NULL == cpu) { + return -ENOMEM; + } + pt->size = PAGE_SIZE; + pt->cpu = cpu; + pt->dma = dma_addr; + + return 0; +} + +int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, + struct scatterlist *list, int sglen ) +{ + __le32 *ptr, fill; + int nr_pages = 0; + int i,p; + + BUG_ON(0 == sglen); + BUG_ON(list->offset > PAGE_SIZE); + + /* if we have a user buffer, the first page may not be + aligned to a page boundary. */ + pt->offset = list->offset; + + ptr = pt->cpu; + for (i = 0; i < sglen; i++, list++) { +/* + pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n", + i, sg_dma_address(list), sg_dma_len(list), + list->offset); +*/ + for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr++) { + *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096); + nr_pages++; + } + } + + + /* safety; fill the page table up with the last valid page */ + fill = *(ptr-1); + for(i=nr_pages;i<1024;i++) { + *ptr++ = fill; + } + +/* + ptr = pt->cpu; + pr_debug("offset: %d\n", pt->offset); + for(i=0;i<5;i++) { + pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]); + } +*/ + return 0; +} + +/********************************************************************************/ +/* interrupt handler */ +static irqreturn_t interrupt_hw(int irq, void *dev_id) +{ + struct saa7146_dev *dev = dev_id; + u32 isr; + u32 ack_isr; + + /* read out the interrupt status register */ + ack_isr = isr = saa7146_read(dev, ISR); + + /* is this our interrupt? */ + if ( 0 == isr ) { + /* nope, some other device */ + return IRQ_NONE; + } + + if (dev->ext) { + if (dev->ext->irq_mask & isr) { + if (dev->ext->irq_func) + dev->ext->irq_func(dev, &isr); + isr &= ~dev->ext->irq_mask; + } + } + if (0 != (isr & (MASK_27))) { + DEB_INT("irq: RPS0 (0x%08x)\n", isr); + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_27; + } + if (0 != (isr & (MASK_28))) { + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_28; + } + if (0 != (isr & (MASK_16|MASK_17))) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + /* only wake up if we expect something */ + if (0 != dev->i2c_op) { + dev->i2c_op = 0; + wake_up(&dev->i2c_wq); + } else { + u32 psr = saa7146_read(dev, PSR); + u32 ssr = saa7146_read(dev, SSR); + pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n", + dev->name, isr, psr, ssr); + } + isr &= ~(MASK_16|MASK_17); + } + if( 0 != isr ) { + ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n", + isr); + ERR("disabling interrupt source(s)!\n"); + SAA7146_IER_DISABLE(dev,isr); + } + saa7146_write(dev, ISR, ack_isr); + return IRQ_HANDLED; +} + +/*********************************************************************************/ +/* configuration-functions */ + +static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) +{ + struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; + struct saa7146_extension *ext = pci_ext->ext; + struct saa7146_dev *dev; + int err = -ENOMEM; + + /* clear out mem for sure */ + dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL); + if (!dev) { + ERR("out of memory\n"); + goto out; + } + + /* create a nice device name */ + sprintf(dev->name, "saa7146 (%d)", saa7146_num); + + DEB_EE("pci:%p\n", pci); + + err = pci_enable_device(pci); + if (err < 0) { + ERR("pci_enable_device() failed\n"); + goto err_free; + } + + /* enable bus-mastering */ + pci_set_master(pci); + + dev->pci = pci; + + /* get chip-revision; this is needed to enable bug-fixes */ + dev->revision = pci->revision; + + /* remap the memory from virtual to physical address */ + + err = pci_request_region(pci, 0, "saa7146"); + if (err < 0) + goto err_disable; + + dev->mem = ioremap(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + if (!dev->mem) { + ERR("ioremap() failed\n"); + err = -ENODEV; + goto err_release; + } + + /* we don't do a master reset here anymore, it screws up + some boards that don't have an i2c-eeprom for configuration + values */ +/* + saa7146_write(dev, MC1, MASK_31); +*/ + + /* disable all irqs */ + saa7146_write(dev, IER, 0); + + /* shut down all dma transfers and rps tasks */ + saa7146_write(dev, MC1, 0x30ff0000); + + /* clear out any rps-signals pending */ + saa7146_write(dev, MC2, 0xf8000000); + + /* request an interrupt for the saa7146 */ + err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED, + dev->name, dev); + if (err < 0) { + ERR("request_irq() failed\n"); + goto err_unmap; + } + + err = -ENOMEM; + + /* get memory for various stuff */ + dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps0.dma_handle, + GFP_KERNEL); + if (!dev->d_rps0.cpu_addr) + goto err_free_irq; + + dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps1.dma_handle, + GFP_KERNEL); + if (!dev->d_rps1.cpu_addr) + goto err_free_rps0; + + dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_i2c.dma_handle, GFP_KERNEL); + if (!dev->d_i2c.cpu_addr) + goto err_free_rps1; + + /* the rest + print status message */ + + pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n", + dev->mem, dev->revision, pci->irq, + pci->subsystem_vendor, pci->subsystem_device); + dev->ext = ext; + + mutex_init(&dev->v4l2_lock); + spin_lock_init(&dev->int_slock); + spin_lock_init(&dev->slock); + + mutex_init(&dev->i2c_lock); + + dev->module = THIS_MODULE; + init_waitqueue_head(&dev->i2c_wq); + + /* set some sane pci arbitrition values */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + /* TODO: use the status code of the callback */ + + err = -ENODEV; + + if (ext->probe && ext->probe(dev)) { + DEB_D("ext->probe() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + + if (ext->attach(dev, pci_ext)) { + DEB_D("ext->attach() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + /* V4L extensions will set the pci drvdata to the v4l2_device in the + attach() above. So for those cards that do not use V4L we have to + set it explicitly. */ + pci_set_drvdata(pci, &dev->v4l2_dev); + + saa7146_num++; + + err = 0; +out: + return err; + +err_free_i2c: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, + dev->d_i2c.dma_handle); +err_free_rps1: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, + dev->d_rps1.dma_handle); +err_free_rps0: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, + dev->d_rps0.dma_handle); +err_free_irq: + free_irq(pci->irq, (void *)dev); +err_unmap: + iounmap(dev->mem); +err_release: + pci_release_region(pci, 0); +err_disable: + pci_disable_device(pci); +err_free: + kfree(dev); + goto out; +} + +static void saa7146_remove_one(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + struct { + void *addr; + dma_addr_t dma; + } dev_map[] = { + { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, + { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, + { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, + { NULL, 0 } + }, *p; + + DEB_EE("dev:%p\n", dev); + + dev->ext->detach(dev); + + /* shut down all video dma transfers */ + saa7146_write(dev, MC1, 0x00ff0000); + + /* disable all irqs, release irq-routine */ + saa7146_write(dev, IER, 0); + + free_irq(pdev->irq, dev); + + for (p = dev_map; p->addr; p++) + dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr, + p->dma); + + iounmap(dev->mem); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + kfree(dev); + + saa7146_num--; +} + +/*********************************************************************************/ +/* extension handling functions */ + +int saa7146_register_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + + ext->driver.name = ext->name; + ext->driver.id_table = ext->pci_tbl; + ext->driver.probe = saa7146_init_one; + ext->driver.remove = saa7146_remove_one; + + pr_info("register extension '%s'\n", ext->name); + return pci_register_driver(&ext->driver); +} + +int saa7146_unregister_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + pr_info("unregister extension '%s'\n", ext->name); + pci_unregister_driver(&ext->driver); + return 0; +} + +EXPORT_SYMBOL_GPL(saa7146_register_extension); +EXPORT_SYMBOL_GPL(saa7146_unregister_extension); + +/* misc functions used by extension modules */ +EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); +EXPORT_SYMBOL_GPL(saa7146_pgtable_free); +EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); +EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); +EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable); +EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); + +EXPORT_SYMBOL_GPL(saa7146_setgpio); + +EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); + +EXPORT_SYMBOL_GPL(saa7146_debug); + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c new file mode 100644 index 000000000000..aa14698a9c54 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "saa7146_vv.h" + +/****************************************************************************/ +/* resource management functions, shamelessly stolen from saa7134 driver */ + +int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + if (fh->resources & bit) { + DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n", + bit, vv->resources); + /* have it already allocated */ + return 1; + } + + /* is it free? */ + if (vv->resources & bit) { + DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n", + vv->resources, bit); + /* no, someone else uses it */ + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + vv->resources |= bit; + DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources); + return 1; +} + +void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + BUG_ON((fh->resources & bits) != bits); + + fh->resources &= ~bits; + vv->resources &= ~bits; + DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources); +} + + +/********************************************************************************/ +/* common dma functions */ + +void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, + struct saa7146_buf *buf) +{ + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + DEB_EE("dev:%p, buf:%p\n", dev, buf); + + videobuf_waiton(q, &buf->vb, 0, 0); + videobuf_dma_unmap(q->dev, dma); + videobuf_dma_free(dma); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + + +/********************************************************************************/ +/* common buffer functions */ + +int saa7146_buffer_queue(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, + struct saa7146_buf *buf) +{ + assert_spin_locked(&dev->slock); + DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf); + + BUG_ON(!q); + + if (NULL == q->curr) { + q->curr = buf; + DEB_D("immediately activating buffer %p\n", buf); + buf->activate(dev,buf,NULL); + } else { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = VIDEOBUF_QUEUED; + DEB_D("adding buffer %p to queue. (active buffer present)\n", + buf); + } + return 0; +} + +void saa7146_buffer_finish(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, + int state) +{ + assert_spin_locked(&dev->slock); + DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); + DEB_EE("q->curr:%p\n", q->curr); + + /* finish current buffer */ + if (NULL == q->curr) { + DEB_D("aiii. no current buffer\n"); + return; + } + + q->curr->vb.state = state; + q->curr->vb.ts = ktime_get_ns(); + wake_up(&q->curr->vb.done); + + q->curr = NULL; +} + +void saa7146_buffer_next(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, int vbi) +{ + struct saa7146_buf *buf,*next = NULL; + + BUG_ON(!q); + + DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi); + + assert_spin_locked(&dev->slock); + if (!list_empty(&q->queue)) { + /* activate next one from queue */ + buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue); + list_del(&buf->vb.queue); + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next,struct saa7146_buf, vb.queue); + q->curr = buf; + DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n", + buf, q->queue.prev, q->queue.next); + buf->activate(dev,buf,next); + } else { + DEB_INT("no next buffer. stopping.\n"); + if( 0 != vbi ) { + /* turn off video-dma3 */ + saa7146_write(dev,MC1, MASK_20); + } else { + /* nothing to do -- just prevent next video-dma1 transfer + by lowering the protection address */ + + // fixme: fix this for vflip != 0 + + saa7146_write(dev, PROT_ADDR1, 0); + saa7146_write(dev, MC2, (MASK_02|MASK_18)); + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_12 | MASK_28)); + +/* + printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); + printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); + printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); + printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); + printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); + printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); +*/ + } + del_timer(&q->timeout); + } +} + +void saa7146_buffer_timeout(struct timer_list *t) +{ + struct saa7146_dmaqueue *q = from_timer(q, t, timeout); + struct saa7146_dev *dev = q->dev; + unsigned long flags; + + DEB_EE("dev:%p, dmaq:%p\n", dev, q); + + spin_lock_irqsave(&dev->slock,flags); + if (q->curr) { + DEB_D("timeout on %p\n", q->curr); + saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR); + } + + /* we don't restart the transfer here like other drivers do. when + a streaming capture is disabled, the timeout function will be + called for the current buffer. if we activate the next buffer now, + we mess up our capture logic. if a timeout occurs on another buffer, + then something is seriously broken before, so no need to buffer the + next capture IMHO... */ +/* + saa7146_buffer_next(dev,q); +*/ + spin_unlock_irqrestore(&dev->slock,flags); +} + +/********************************************************************************/ +/* file operations */ + +static int fops_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_dev *dev = video_drvdata(file); + struct saa7146_fh *fh = NULL; + int result = 0; + + DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + + DEB_D("using: %p\n", dev); + + /* check if an extension is registered */ + if( NULL == dev->ext ) { + DEB_S("no extension registered for this device\n"); + result = -ENODEV; + goto out; + } + + /* allocate per open data */ + fh = kzalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) { + DEB_S("cannot allocate memory for per open data\n"); + result = -ENOMEM; + goto out; + } + + v4l2_fh_init(&fh->fh, vdev); + + file->private_data = &fh->fh; + fh->dev = dev; + + if (vdev->vfl_type == VFL_TYPE_VBI) { + DEB_S("initializing vbi...\n"); + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + result = saa7146_vbi_uops.open(dev,file); + if (dev->ext_vv_data->vbi_fops.open) + dev->ext_vv_data->vbi_fops.open(file); + } else { + DEB_S("initializing video...\n"); + result = saa7146_video_uops.open(dev,file); + } + + if (0 != result) { + goto out; + } + + if( 0 == try_module_get(dev->ext->module)) { + result = -EINVAL; + goto out; + } + + result = 0; + v4l2_fh_add(&fh->fh); +out: + if (fh && result != 0) { + kfree(fh); + file->private_data = NULL; + } + mutex_unlock(vdev->lock); + return result; +} + +static int fops_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + + DEB_EE("file:%p\n", file); + + mutex_lock(vdev->lock); + + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + saa7146_vbi_uops.release(dev,file); + if (dev->ext_vv_data->vbi_fops.release) + dev->ext_vv_data->vbi_fops.release(file); + } else { + saa7146_video_uops.release(dev,file); + } + + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + module_put(dev->ext->module); + file->private_data = NULL; + kfree(fh); + + mutex_unlock(vdev->lock); + + return 0; +} + +static int fops_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct videobuf_queue *q; + int res; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: { + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", + file, vma); + q = &fh->video_q; + break; + } + case VFL_TYPE_VBI: { + DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", + file, vma); + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return -ENODEV; + q = &fh->vbi_q; + break; + } + default: + BUG(); + } + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(q, vma); + mutex_unlock(vdev->lock); + return res; +} + +static __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct videobuf_buffer *buf = NULL; + struct videobuf_queue *q; + __poll_t res = v4l2_ctrl_poll(file, wait); + + DEB_EE("file:%p, poll:%p\n", file, wait); + + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return res | EPOLLOUT | EPOLLWRNORM; + if( 0 == fh->vbi_q.streaming ) + return res | videobuf_poll_stream(file, &fh->vbi_q, wait); + q = &fh->vbi_q; + } else { + DEB_D("using video queue\n"); + q = &fh->video_q; + } + + if (!list_empty(&q->stream)) + buf = list_entry(q->stream.next, struct videobuf_buffer, stream); + + if (!buf) { + DEB_D("buf == NULL!\n"); + return res | EPOLLERR; + } + + poll_wait(file, &buf->done, wait); + if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { + DEB_D("poll succeeded!\n"); + return res | EPOLLIN | EPOLLRDNORM; + } + + DEB_D("nothing to poll for, buf->state:%d\n", buf->state); + return res; +} + +static __poll_t fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + __poll_t res; + + mutex_lock(vdev->lock); + res = __fops_poll(file, wait); + mutex_unlock(vdev->lock); + return res; +} + +static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + int ret; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: +/* + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", + file, data, (unsigned long)count); +*/ + return saa7146_video_uops.read(file,data,count,ppos); + case VFL_TYPE_VBI: +/* + DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", + file, data, (unsigned long)count); +*/ + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = saa7146_vbi_uops.read(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + return -EINVAL; + default: + BUG(); + } +} + +static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + int ret; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: + return -EINVAL; + case VFL_TYPE_VBI: + if (fh->dev->ext_vv_data->vbi_fops.write) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + return -EINVAL; + default: + BUG(); + } +} + +static const struct v4l2_file_operations video_fops = +{ + .owner = THIS_MODULE, + .open = fops_open, + .release = fops_release, + .read = fops_read, + .write = fops_write, + .poll = fops_poll, + .mmap = fops_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static void vv_callback(struct saa7146_dev *dev, unsigned long status) +{ + u32 isr = status; + + DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status); + + if (0 != (isr & (MASK_27))) { + DEB_INT("irq: RPS0 (0x%08x)\n", isr); + saa7146_video_uops.irq_done(dev,isr); + } + + if (0 != (isr & (MASK_28))) { + u32 mc2 = saa7146_read(dev, MC2); + if( 0 != (mc2 & MASK_15)) { + DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr); + wake_up(&dev->vv_data->vbi_wq); + saa7146_write(dev,MC2, MASK_31); + return; + } + DEB_INT("irq: RPS1 (0x%08x)\n", isr); + saa7146_vbi_uops.irq_done(dev,isr); + } +} + +static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { + .s_ctrl = saa7146_s_ctrl, +}; + +int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) +{ + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_pix_format *fmt; + struct v4l2_vbi_format *vbi; + struct saa7146_vv *vv; + int err; + + err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); + if (err) + return err; + + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (hdl->error) { + err = hdl->error; + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return err; + } + dev->v4l2_dev.ctrl_handler = hdl; + + vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); + if (vv == NULL) { + ERR("out of memory. aborting.\n"); + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return -ENOMEM; + } + ext_vv->vid_ops = saa7146_video_ioctl_ops; + ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; + ext_vv->core_ops = &saa7146_video_ioctl_ops; + + DEB_EE("dev:%p\n", dev); + + /* set default values for video parts of the saa7146 */ + saa7146_write(dev, BCS_CTRL, 0x80400040); + + /* enable video-port pins */ + saa7146_write(dev, MC1, (MASK_10 | MASK_26)); + + /* save per-device extension data (one extension can + handle different devices that might need different + configuration data) */ + dev->ext_vv_data = ext_vv; + + vv->d_clipping.cpu_addr = + dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + &vv->d_clipping.dma_handle, GFP_KERNEL); + if( NULL == vv->d_clipping.cpu_addr ) { + ERR("out of memory. aborting.\n"); + kfree(vv); + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return -ENOMEM; + } + + saa7146_video_uops.init(dev,vv); + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + saa7146_vbi_uops.init(dev,vv); + + vv->ov_fb.fmt.width = vv->standard->h_max_out; + vv->ov_fb.fmt.height = vv->standard->v_max_out; + vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width; + vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height; + vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; + + fmt = &vv->video_fmt; + fmt->width = 384; + fmt->height = 288; + fmt->pixelformat = V4L2_PIX_FMT_BGR24; + fmt->field = V4L2_FIELD_ANY; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->bytesperline = 3 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + + vbi = &vv->vbi_fmt; + vbi->sampling_rate = 27000000; + vbi->offset = 248; /* todo */ + vbi->samples_per_line = 720 * 2; + vbi->sample_format = V4L2_PIX_FMT_GREY; + + /* fixme: this only works for PAL */ + vbi->start[0] = 5; + vbi->count[0] = 16; + vbi->start[1] = 312; + vbi->count[1] = 16; + + timer_setup(&vv->vbi_read_timeout, NULL, 0); + + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; + vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; + dev->vv_data = vv; + dev->vv_callback = &vv_callback; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_vv_init); + +int saa7146_vv_release(struct saa7146_dev* dev) +{ + struct saa7146_vv *vv = dev->vv_data; + + DEB_EE("dev:%p\n", dev); + + v4l2_device_unregister(&dev->v4l2_dev); + dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + kfree(vv); + dev->vv_data = NULL; + dev->vv_callback = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_vv_release); + +int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, + char *name, int type) +{ + int err; + int i; + + DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); + + vfd->fops = &video_fops; + if (type == VFL_TYPE_VIDEO) + vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; + else + vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; + vfd->release = video_device_release_empty; + vfd->lock = &dev->v4l2_lock; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->tvnorms = 0; + for (i = 0; i < dev->ext_vv_data->num_stds; i++) + vfd->tvnorms |= dev->ext_vv_data->stds[i].id; + strscpy(vfd->name, name, sizeof(vfd->name)); + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + vfd->device_caps |= dev->ext_vv_data->capabilities; + if (type == VFL_TYPE_VIDEO) + vfd->device_caps &= + ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); + else + vfd->device_caps &= + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); + video_set_drvdata(vfd, dev); + + err = video_register_device(vfd, type, -1); + if (err < 0) { + ERR("cannot register v4l2 device. skipping.\n"); + return err; + } + + pr_info("%s: registered device %s [v4l2]\n", + dev->name, video_device_node_name(vfd)); + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_register_device); + +int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev) +{ + DEB_EE("dev:%p\n", dev); + + video_unregister_device(vfd); + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_unregister_device); + +static int __init saa7146_vv_init_module(void) +{ + return 0; +} + + +static void __exit saa7146_vv_cleanup_module(void) +{ +} + +module_init(saa7146_vv_init_module); +module_exit(saa7146_vv_cleanup_module); + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c new file mode 100644 index 000000000000..b1222a4cfa4a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c @@ -0,0 +1,1046 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "saa7146_vv.h" + +static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format) +{ + /* clear out the necessary bits */ + *clip_format &= 0x0000ffff; + /* set these bits new */ + *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16)); +} + +static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl) +{ + *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28); + *hps_ctrl |= (source << 30) | (sync << 28); +} + +static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl) +{ + int hyo = 0, hxo = 0; + + hyo = vv->standard->v_offset; + hxo = vv->standard->h_offset; + + *hps_h_scale &= ~(MASK_B0 | 0xf00); + *hps_h_scale |= (hxo << 0); + + *hps_ctrl &= ~(MASK_W0 | MASK_B2); + *hps_ctrl |= (hyo << 12); +} + +/* helper functions for the calculation of the horizontal- and vertical + scaling registers, clip-format-register etc ... + these functions take pointers to the (most-likely read-out + original-values) and manipulate them according to the requested + changes. +*/ + +/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */ +static struct { + u16 hps_coeff; + u16 weight_sum; +} hps_h_coeff_tab [] = { + {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8}, + {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8}, + {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8}, + {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8}, + {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8}, + {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8}, + {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8}, + {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, + {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8}, + {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16} +}; + +/* table of attenuation values for horizontal scaling */ +static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; + +/* calculate horizontal scale registers */ +static int calculate_h_scale_registers(struct saa7146_dev *dev, + int in_x, int out_x, int flip_lr, + u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale) +{ + /* horizontal prescaler */ + u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0; + /* horizontal scaler */ + u32 xim = 0, xp = 0, xsci =0; + /* vertical scale & gain */ + u32 pfuv = 0; + + /* helper variables */ + u32 h_atten = 0, i = 0; + + if ( 0 == out_x ) { + return -EINVAL; + } + + /* mask out vanity-bit */ + *hps_ctrl &= ~MASK_29; + + /* calculate prescale-(xspc)-value: [n .. 1/2) : 1 + [1/2 .. 1/3) : 2 + [1/3 .. 1/4) : 3 + ... */ + if (in_x > out_x) { + xpsc = in_x / out_x; + } + else { + /* zooming */ + xpsc = 1; + } + + /* if flip_lr-bit is set, number of pixels after + horizontal prescaling must be < 384 */ + if ( 0 != flip_lr ) { + + /* set vanity bit */ + *hps_ctrl |= MASK_29; + + while (in_x / xpsc >= 384 ) + xpsc++; + } + /* if zooming is wanted, number of pixels after + horizontal prescaling must be < 768 */ + else { + while ( in_x / xpsc >= 768 ) + xpsc++; + } + + /* maximum prescale is 64 (p.69) */ + if ( xpsc > 64 ) + xpsc = 64; + + /* keep xacm clear*/ + xacm = 0; + + /* set horizontal filter parameters (CXY = CXUV) */ + cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff; + cxuv = cxy; + + /* calculate and set horizontal fine scale (xsci) */ + + /* bypass the horizontal scaler ? */ + if ( (in_x == out_x) && ( 1 == xpsc ) ) + xsci = 0x400; + else + xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc; + + /* set start phase for horizontal fine scale (xp) to 0 */ + xp = 0; + + /* set xim, if we bypass the horizontal scaler */ + if ( 0x400 == xsci ) + xim = 1; + else + xim = 0; + + /* if the prescaler is bypassed, enable horizontal + accumulation mode (xacm) and clear dcgx */ + if( 1 == xpsc ) { + xacm = 1; + dcgx = 0; + } else { + xacm = 0; + /* get best match in the table of attenuations + for horizontal scaling */ + h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum; + + for (i = 0; h_attenuation[i] != 0; i++) { + if (h_attenuation[i] >= h_atten) + break; + } + + dcgx = i; + } + + /* the horizontal scaling increment controls the UV filter + to reduce the bandwidth to improve the display quality, + so set it ... */ + if ( xsci == 0x400) + pfuv = 0x00; + else if ( xsci < 0x600) + pfuv = 0x01; + else if ( xsci < 0x680) + pfuv = 0x11; + else if ( xsci < 0x700) + pfuv = 0x22; + else + pfuv = 0x33; + + + *hps_v_gain &= MASK_W0|MASK_B2; + *hps_v_gain |= (pfuv << 24); + + *hps_h_scale &= ~(MASK_W1 | 0xf000); + *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12); + + *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0); + + return 0; +} + +static struct { + u16 hps_coeff; + u16 weight_sum; +} hps_v_coeff_tab [] = { + {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8}, + {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16}, + {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16}, + {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32}, + {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32}, + {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32}, + {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64}, + {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64}, + {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64}, + {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128} +}; + +/* table of attenuation values for vertical scaling */ +static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; + +/* calculate vertical scale registers */ +static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field, + int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain) +{ + int lpi = 0; + + /* vertical scaling */ + u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0; + /* vertical scale & gain */ + u32 dcgy = 0, cya_cyb = 0; + + /* helper variables */ + u32 v_atten = 0, i = 0; + + /* error, if vertical zooming */ + if ( in_y < out_y ) { + return -EINVAL; + } + + /* linear phase interpolation may be used + if scaling is between 1 and 1/2 (both fields used) + or scaling is between 1/2 and 1/4 (if only one field is used) */ + + if (V4L2_FIELD_HAS_BOTH(field)) { + if( 2*out_y >= in_y) { + lpi = 1; + } + } else if (field == V4L2_FIELD_TOP + || field == V4L2_FIELD_ALTERNATE + || field == V4L2_FIELD_BOTTOM) { + if( 4*out_y >= in_y ) { + lpi = 1; + } + out_y *= 2; + } + if( 0 != lpi ) { + + yacm = 0; + yacl = 0; + cya_cyb = 0x00ff; + + /* calculate scaling increment */ + if ( in_y > out_y ) + ysci = ((1024 * in_y) / (out_y + 1)) - 1024; + else + ysci = 0; + + dcgy = 0; + + /* calculate ype and ypo */ + ype = ysci / 16; + ypo = ype + (ysci / 64); + + } else { + yacm = 1; + + /* calculate scaling increment */ + ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10; + + /* calculate ype and ypo */ + ypo = ype = ((ysci + 15) / 16); + + /* the sequence length interval (yacl) has to be set according + to the prescale value, e.g. [n .. 1/2) : 0 + [1/2 .. 1/3) : 1 + [1/3 .. 1/4) : 2 + ... */ + if ( ysci < 512) { + yacl = 0; + } else { + yacl = ( ysci / (1024 - ysci) ); + } + + /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ + cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff; + + /* get best match in the table of attenuations for vertical scaling */ + v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum; + + for (i = 0; v_attenuation[i] != 0; i++) { + if (v_attenuation[i] >= v_atten) + break; + } + + dcgy = i; + } + + /* ypo and ype swapped in spec ? */ + *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1); + + *hps_v_gain &= ~(MASK_W0|MASK_B2); + *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0); + + return 0; +} + +/* simple bubble-sort algorithm with duplicate elimination */ +static int sort_and_eliminate(u32* values, int* count) +{ + int low = 0, high = 0, top = 0; + int cur = 0, next = 0; + + /* sanity checks */ + if( (0 > *count) || (NULL == values) ) { + return -EINVAL; + } + + /* bubble sort the first @count items of the array @values */ + for( top = *count; top > 0; top--) { + for( low = 0, high = 1; high < top; low++, high++) { + if( values[low] > values[high] ) + swap(values[low], values[high]); + } + } + + /* remove duplicate items */ + for( cur = 0, next = 1; next < *count; next++) { + if( values[cur] != values[next]) + values[++cur] = values[next]; + } + + *count = cur + 1; + + return 0; +} + +static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh, + struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) +{ + struct saa7146_vv *vv = dev->vv_data; + __le32 *clipping = vv->d_clipping.cpu_addr; + + int width = vv->ov.win.w.width; + int height = vv->ov.win.w.height; + int clipcount = vv->ov.nclips; + + u32 line_list[32]; + u32 pixel_list[32]; + int numdwords = 0; + + int i = 0, j = 0; + int cnt_line = 0, cnt_pixel = 0; + + int x[32], y[32], w[32], h[32]; + + /* clear out memory */ + memset(&line_list[0], 0x00, sizeof(u32)*32); + memset(&pixel_list[0], 0x00, sizeof(u32)*32); + memset(clipping, 0x00, SAA7146_CLIPPING_MEM); + + /* fill the line and pixel-lists */ + for(i = 0; i < clipcount; i++) { + int l = 0, r = 0, t = 0, b = 0; + + x[i] = vv->ov.clips[i].c.left; + y[i] = vv->ov.clips[i].c.top; + w[i] = vv->ov.clips[i].c.width; + h[i] = vv->ov.clips[i].c.height; + + if( w[i] < 0) { + x[i] += w[i]; w[i] = -w[i]; + } + if( h[i] < 0) { + y[i] += h[i]; h[i] = -h[i]; + } + if( x[i] < 0) { + w[i] += x[i]; x[i] = 0; + } + if( y[i] < 0) { + h[i] += y[i]; y[i] = 0; + } + if( 0 != vv->vflip ) { + y[i] = height - y[i] - h[i]; + } + + l = x[i]; + r = x[i]+w[i]; + t = y[i]; + b = y[i]+h[i]; + + /* insert left/right coordinates */ + pixel_list[ 2*i ] = min_t(int, l, width); + pixel_list[(2*i)+1] = min_t(int, r, width); + /* insert top/bottom coordinates */ + line_list[ 2*i ] = min_t(int, t, height); + line_list[(2*i)+1] = min_t(int, b, height); + } + + /* sort and eliminate lists */ + cnt_line = cnt_pixel = 2*clipcount; + sort_and_eliminate( &pixel_list[0], &cnt_pixel ); + sort_and_eliminate( &line_list[0], &cnt_line ); + + /* calculate the number of used u32s */ + numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2; + numdwords = max_t(int, 4, numdwords); + numdwords = min_t(int, 64, numdwords); + + /* fill up cliptable */ + for(i = 0; i < cnt_pixel; i++) { + clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16); + } + for(i = 0; i < cnt_line; i++) { + clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16); + } + + /* fill up cliptable with the display infos */ + for(j = 0; j < clipcount; j++) { + + for(i = 0; i < cnt_pixel; i++) { + + if( x[j] < 0) + x[j] = 0; + + if( pixel_list[i] < (x[j] + w[j])) { + + if ( pixel_list[i] >= x[j] ) { + clipping[2*i] |= cpu_to_le32(1 << j); + } + } + } + for(i = 0; i < cnt_line; i++) { + + if( y[j] < 0) + y[j] = 0; + + if( line_list[i] < (y[j] + h[j]) ) { + + if( line_list[i] >= y[j] ) { + clipping[(2*i)+1] |= cpu_to_le32(1 << j); + } + } + } + } + + /* adjust arbitration control register */ + *arbtr_ctrl &= 0xffff00ff; + *arbtr_ctrl |= 0x00001c00; + + vdma2->base_even = vv->d_clipping.dma_handle; + vdma2->base_odd = vv->d_clipping.dma_handle; + vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords)); + vdma2->base_page = 0x04; + vdma2->pitch = 0x00; + vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) ); + + /* set clipping-mode. this depends on the field(s) used */ + *clip_format &= 0xfffffff7; + if (V4L2_FIELD_HAS_BOTH(field)) { + *clip_format |= 0x00000008; + } else { + *clip_format |= 0x00000000; + } +} + +/* disable clipping */ +static void saa7146_disable_clipping(struct saa7146_dev *dev) +{ + u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + + /* mask out relevant bits (=lower word)*/ + clip_format &= MASK_W1; + + /* upload clipping-registers*/ + saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); + + /* disable video dma2 */ + saa7146_write(dev, MC1, MASK_21); +} + +static void saa7146_set_clipping_rect(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + enum v4l2_field field = vv->ov.win.field; + struct saa7146_video_dma vdma2; + u32 clip_format; + u32 arbtr_ctrl; + + /* check clipcount, disable clipping if clipcount == 0*/ + if (vv->ov.nclips == 0) { + saa7146_disable_clipping(dev); + return; + } + + clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); + + calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field); + + /* set clipping format */ + clip_format &= 0xffff0008; + clip_format |= (SAA7146_CLIPPING_RECT << 4); + + /* prepare video dma2 */ + saa7146_write(dev, BASE_EVEN2, vdma2.base_even); + saa7146_write(dev, BASE_ODD2, vdma2.base_odd); + saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr); + saa7146_write(dev, BASE_PAGE2, vdma2.base_page); + saa7146_write(dev, PITCH2, vdma2.pitch); + saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte); + + /* prepare the rest */ + saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); + saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); + + /* upload clip_control-register, clipping-registers, enable video dma2 */ + saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19)); + saa7146_write(dev, MC1, (MASK_05 | MASK_21)); +} + +static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) +{ + struct saa7146_vv *vv = dev->vv_data; + + int source = vv->current_hps_source; + int sync = vv->current_hps_sync; + + u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0; + + /* set vertical scale */ + hps_v_scale = 0; /* all bits get set by the function-call */ + hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/ + calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain); + + /* set horizontal scale */ + hps_ctrl = 0; + hps_h_prescale = 0; /* all bits get set in the function */ + hps_h_scale = 0; + calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale); + + /* set hyo and hxo */ + calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl); + calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl); + + /* write out new register contents */ + saa7146_write(dev, HPS_V_SCALE, hps_v_scale); + saa7146_write(dev, HPS_V_GAIN, hps_v_gain); + saa7146_write(dev, HPS_CTRL, hps_ctrl); + saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale); + saa7146_write(dev, HPS_H_SCALE, hps_h_scale); + + /* upload shadow-ram registers */ + saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); +} + +/* calculate the new memory offsets for a desired position */ +static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat); + + int b_depth = vv->ov_fmt->depth; + int b_bpl = vv->ov_fb.fmt.bytesperline; + /* The unsigned long cast is to remove a 64-bit compile warning since + it looks like a 64-bit address is cast to a 32-bit value, even + though the base pointer is really a 32-bit physical address that + goes into a 32-bit DMA register. + FIXME: might not work on some 64-bit platforms, but see the FIXME + in struct v4l2_framebuffer (videodev2.h) for that. + */ + u32 base = (u32)(unsigned long)vv->ov_fb.base; + + struct saa7146_video_dma vdma1; + + /* calculate memory offsets for picture, look if we shall top-down-flip */ + vdma1.pitch = 2*b_bpl; + if ( 0 == vv->vflip ) { + vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); + vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2); + vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2)); + } + else { + vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); + vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2); + vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2)); + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + + if ( 0 != vv->vflip ) { + vdma1.pitch *= -1; + } + + vdma1.base_page = sfmt->swap; + vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels; + + saa7146_write_out_dma(dev, 1, &vdma1); +} + +static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) +{ + u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + + /* call helper function */ + calculate_output_format_register(dev,palette,&clip_format); + + /* update the hps registers */ + saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); +} + +/* select input-source */ +void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync) +{ + struct saa7146_vv *vv = dev->vv_data; + u32 hps_ctrl = 0; + + /* read old state */ + hps_ctrl = saa7146_read(dev, HPS_CTRL); + + hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 ); + hps_ctrl |= (source << 30) | (sync << 28); + + /* write back & upload register */ + saa7146_write(dev, HPS_CTRL, hps_ctrl); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); + + vv->current_hps_source = source; + vv->current_hps_sync = sync; +} +EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); + +int saa7146_enable_overlay(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); + saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); + saa7146_set_output_format(dev, vv->ov_fmt->trans); + saa7146_set_clipping_rect(fh); + + /* enable video dma1 */ + saa7146_write(dev, MC1, (MASK_06 | MASK_22)); + return 0; +} + +void saa7146_disable_overlay(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + + /* disable clipping + video dma1 */ + saa7146_disable_clipping(dev); + saa7146_write(dev, MC1, MASK_22); +} + +void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) +{ + int where = 0; + + if( which < 1 || which > 3) { + return; + } + + /* calculate starting address */ + where = (which-1)*0x18; + + saa7146_write(dev, where, vdma->base_odd); + saa7146_write(dev, where+0x04, vdma->base_even); + saa7146_write(dev, where+0x08, vdma->prot_addr); + saa7146_write(dev, where+0x0c, vdma->pitch); + saa7146_write(dev, where+0x10, vdma->base_page); + saa7146_write(dev, where+0x14, vdma->num_line_byte); + + /* upload */ + saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1))); +/* + printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even); + printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd); + printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr); + printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page); + printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch); + printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte); +*/ +} + +static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_video_dma vdma1; + + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + int width = buf->fmt->width; + int height = buf->fmt->height; + int bytesperline = buf->fmt->bytesperline; + enum v4l2_field field = buf->fmt->field; + + int depth = sfmt->depth; + + DEB_CAP("[size=%dx%d,fields=%s]\n", + width, height, v4l2_field_names[field]); + + if( bytesperline != 0) { + vdma1.pitch = bytesperline*2; + } else { + vdma1.pitch = (width*depth*2)/8; + } + vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); + vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap; + + if( 0 != vv->vflip ) { + vdma1.prot_addr = buf->pt[0].offset; + vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height; + vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); + } else { + vdma1.base_even = buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); + vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height; + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + if ( vv->last_field == V4L2_FIELD_TOP ) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + + if( 0 != vv->vflip ) { + vdma1.pitch *= -1; + } + + saa7146_write_out_dma(dev, 1, &vdma1); + return 0; +} + +static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) +{ + int height = buf->fmt->height; + int width = buf->fmt->width; + + vdma2->pitch = width; + vdma3->pitch = width; + + /* fixme: look at bytesperline! */ + + if( 0 != vv->vflip ) { + vdma2->prot_addr = buf->pt[1].offset; + vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); + + vdma3->prot_addr = buf->pt[2].offset; + vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); + } else { + vdma3->base_even = buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2); + vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; + + vdma2->base_even = buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2); + vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; + } + + return 0; +} + +static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) +{ + int height = buf->fmt->height; + int width = buf->fmt->width; + + vdma2->pitch = width/2; + vdma3->pitch = width/2; + + if( 0 != vv->vflip ) { + vdma2->prot_addr = buf->pt[2].offset; + vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset; + vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); + + vdma3->prot_addr = buf->pt[1].offset; + vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset; + vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); + + } else { + vdma3->base_even = buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even + (vdma3->pitch); + vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; + + vdma2->base_even = buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even + (vdma2->pitch); + vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; + } + return 0; +} + +static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_video_dma vdma1; + struct saa7146_video_dma vdma2; + struct saa7146_video_dma vdma3; + + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + int width = buf->fmt->width; + int height = buf->fmt->height; + enum v4l2_field field = buf->fmt->field; + + BUG_ON(0 == buf->pt[0].dma); + BUG_ON(0 == buf->pt[1].dma); + BUG_ON(0 == buf->pt[2].dma); + + DEB_CAP("[size=%dx%d,fields=%s]\n", + width, height, v4l2_field_names[field]); + + /* fixme: look at bytesperline! */ + + /* fixme: what happens for user space buffers here?. The offsets are + most likely wrong, this version here only works for page-aligned + buffers, modifications to the pagetable-functions are necessary...*/ + + vdma1.pitch = width*2; + vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); + vdma1.base_page = buf->pt[0].dma | ME1; + + if( 0 != vv->vflip ) { + vdma1.prot_addr = buf->pt[0].offset; + vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); + } else { + vdma1.base_even = buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); + vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset; + } + + vdma2.num_line_byte = 0; /* unused */ + vdma2.base_page = buf->pt[1].dma | ME1; + + vdma3.num_line_byte = 0; /* unused */ + vdma3.base_page = buf->pt[2].dma | ME1; + + switch( sfmt->depth ) { + case 12: { + calc_planar_420(vv,buf,&vdma2,&vdma3); + break; + } + case 16: { + calc_planar_422(vv,buf,&vdma2,&vdma3); + break; + } + default: { + return -1; + } + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.prot_addr; + vdma3.pitch /= 2; + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.prot_addr; + vdma3.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.base_even; + vdma2.base_even = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.base_even; + vdma3.base_even = vdma3.prot_addr; + vdma3.pitch /= 2; + } + + if( 0 != vv->vflip ) { + vdma1.pitch *= -1; + vdma2.pitch *= -1; + vdma3.pitch *= -1; + } + + saa7146_write_out_dma(dev, 1, &vdma1); + if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) { + saa7146_write_out_dma(dev, 3, &vdma2); + saa7146_write_out_dma(dev, 2, &vdma3); + } else { + saa7146_write_out_dma(dev, 2, &vdma2); + saa7146_write_out_dma(dev, 3, &vdma3); + } + return 0; +} + +static void program_capture_engine(struct saa7146_dev *dev, int planar) +{ + struct saa7146_vv *vv = dev->vv_data; + int count = 0; + + unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; + unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; + + /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/ + WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait); + WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait); + + /* set rps register 0 */ + WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4)); + WRITE_RPS0(MASK_27 | MASK_11); + + /* turn on video-dma1 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_06 | MASK_22); /* => mask */ + WRITE_RPS0(MASK_06 | MASK_22); /* => values */ + if( 0 != planar ) { + /* turn on video-dma2 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ + WRITE_RPS0(MASK_05 | MASK_21); /* => values */ + + /* turn on video-dma3 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS0(MASK_04 | MASK_20); /* => values */ + } + + /* wait for o_fid_a/b / e_fid_a/b toggle */ + if ( vv->last_field == V4L2_FIELD_INTERLACED ) { + WRITE_RPS0(CMD_PAUSE | o_wait); + WRITE_RPS0(CMD_PAUSE | e_wait); + } else if ( vv->last_field == V4L2_FIELD_TOP ) { + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); + WRITE_RPS0(CMD_PAUSE | o_wait); + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); + WRITE_RPS0(CMD_PAUSE | e_wait); + } + + /* turn off video-dma1 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_22 | MASK_06); /* => mask */ + WRITE_RPS0(MASK_22); /* => values */ + if( 0 != planar ) { + /* turn off video-dma2 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ + WRITE_RPS0(MASK_21); /* => values */ + + /* turn off video-dma3 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS0(MASK_20); /* => values */ + } + + /* generate interrupt */ + WRITE_RPS0(CMD_INTERRUPT); + + /* stop */ + WRITE_RPS0(CMD_STOP); +} + +void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) +{ + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + struct saa7146_vv *vv = dev->vv_data; + u32 vdma1_prot_addr; + + DEB_CAP("buf:%p, next:%p\n", buf, next); + + vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1); + if( 0 == vdma1_prot_addr ) { + /* clear out beginning of streaming bit (rps register 0)*/ + DEB_CAP("forcing sync to new frame\n"); + saa7146_write(dev, MC2, MASK_27 ); + } + + saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field); + saa7146_set_output_format(dev, sfmt->trans); + saa7146_disable_clipping(dev); + + if ( vv->last_field == V4L2_FIELD_INTERLACED ) { + } else if ( vv->last_field == V4L2_FIELD_TOP ) { + vv->last_field = V4L2_FIELD_BOTTOM; + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + vv->last_field = V4L2_FIELD_TOP; + } + + if( 0 != IS_PLANAR(sfmt->trans)) { + calculate_video_dma_grab_planar(dev, buf); + program_capture_engine(dev,1); + } else { + calculate_video_dma_grab_packed(dev, buf); + program_capture_engine(dev,0); + } + +/* + printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); + printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); + printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); + printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); + printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); + printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); + printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1)); +*/ + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); + + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_12 | MASK_28)); +} diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c new file mode 100644 index 000000000000..7a33fe51775a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "saa7146_vv.h" + +static u32 saa7146_i2c_func(struct i2c_adapter *adapter) +{ + /* DEB_I2C("'%s'\n", adapter->name); */ + + return I2C_FUNC_I2C + | I2C_FUNC_SMBUS_QUICK + | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + +/* this function returns the status-register of our i2c-device */ +static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) +{ + u32 iicsta = saa7146_read(dev, I2C_STATUS); + /* DEB_I2C("status: 0x%08x\n", iicsta); */ + return iicsta; +} + +/* this function runs through the i2c-messages and prepares the data to be + sent through the saa7146. have a look at the specifications p. 122 ff + to understand this. it returns the number of u32s to send, or -1 + in case of an error. */ +static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) +{ + int h1, h2; + int i, j, addr; + int mem = 0, op_count = 0; + + /* first determine size of needed memory */ + for(i = 0; i < num; i++) { + mem += m[i].len + 1; + } + + /* worst case: we need one u32 for three bytes to be send + plus one extra byte to address the device */ + mem = 1 + ((mem-1) / 3); + + /* we assume that op points to a memory of at least + * SAA7146_I2C_MEM bytes size. if we exceed this limit... + */ + if ((4 * mem) > SAA7146_I2C_MEM) { + /* DEB_I2C("cannot prepare i2c-message\n"); */ + return -ENOMEM; + } + + /* be careful: clear out the i2c-mem first */ + memset(op,0,sizeof(__le32)*mem); + + /* loop through all messages */ + for(i = 0; i < num; i++) { + + 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)); + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* insert the data bytes */ + h1 = op_count/3; h2 = op_count%3; + op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); + op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); + op_count++; + } + + } + + /* have a look at the last byte inserted: + if it was: ...CONT change it to ...STOP */ + h1 = (op_count-1)/3; h2 = (op_count-1)%3; + if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { + op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); + op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); + } + + /* return the number of u32s to send */ + return mem; +} + +/* this functions loops through all i2c-messages. normally, it should determine + which bytes were read through the adapter and write them back to the corresponding + i2c-message. but instead, we simply write back all bytes. + fixme: this could be improved. */ +static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) +{ + int i, j; + int op_count = 0; + + /* loop through all messages */ + for(i = 0; i < num; i++) { + + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* write back all bytes that could have been read */ + m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); + op_count++; + } + } + + return 0; +} + +/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ +static int saa7146_i2c_reset(struct saa7146_dev *dev) +{ + /* get current status */ + u32 status = saa7146_i2c_status(dev); + + /* clear registers for sure */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, 0); + + /* check if any operation is still in progress */ + if ( 0 != ( status & SAA7146_I2C_BUSY) ) { + + /* yes, kill ongoing operation */ + DEB_I2C("busy_state detected\n"); + + /* set "ABORT-OPERATION"-bit (bit 7)*/ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending; this is needed because p.123, note 1 */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ + status = saa7146_i2c_status(dev); + + if ( dev->i2c_bitrate != status ) { + + DEB_I2C("error_state detected. status:0x%08x\n", status); + + /* Repeat the abort operation. This seems to be necessary + after serious protocol errors caused by e.g. the SAA7740 */ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* the data sheet says it might be necessary to clear the status + twice after an abort */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* if any error is still present, a fatal error has occurred ... */ + status = saa7146_i2c_status(dev); + if ( dev->i2c_bitrate != status ) { + DEB_I2C("fatal error. status:0x%08x\n", status); + return -1; + } + + return 0; +} + +/* this functions writes out the data-byte 'dword' to the i2c-device. + it returns 0 if ok, -1 if the transfer failed, -2 if the transfer + failed badly (e.g. address error) */ +static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) +{ + u32 status = 0, mc2 = 0; + int trial = 0; + unsigned long timeout; + + /* write out i2c-command */ + DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n", + *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op); + + if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { + + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + + dev->i2c_op = 1; + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + timeout = HZ/100 + 1; /* 10ms */ + timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); + if (timeout == -ERESTARTSYS || dev->i2c_op) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + if (timeout == -ERESTARTSYS) + /* a signal arrived */ + return -ERESTARTSYS; + + pr_warn("%s %s [irq]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + status = saa7146_read(dev, I2C_STATUS); + } else { + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + /* do not poll for i2c-status before upload is complete */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + while(1) { + mc2 = (saa7146_read(dev, MC2) & 0x1); + if( 0 != mc2 ) { + break; + } + if (time_after(jiffies,timeout)) { + pr_warn("%s %s: timed out waiting for MC2\n", + dev->name, __func__); + return -EIO; + } + } + /* wait until we get a transfer done or error */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + /* first read usually delivers bogus results... */ + saa7146_i2c_status(dev); + while(1) { + status = saa7146_i2c_status(dev); + if ((status & 0x3) != 1) + break; + if (time_after(jiffies,timeout)) { + /* this is normal when probing the bus + * (no answer from nonexisistant device...) + */ + pr_warn("%s %s [poll]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + if (++trial < 50 && short_delay) + udelay(10); + else + msleep(1); + } + } + + /* give a detailed status report */ + if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR | + SAA7146_I2C_DTERR | SAA7146_I2C_DRERR | + SAA7146_I2C_AL | SAA7146_I2C_ERR | + SAA7146_I2C_BUSY)) ) { + + if ( 0 == (status & SAA7146_I2C_ERR) || + 0 == (status & SAA7146_I2C_BUSY) ) { + /* it may take some time until ERR goes high - ignore */ + DEB_I2C("unexpected i2c status %04x\n", status); + } + if( 0 != (status & SAA7146_I2C_SPERR) ) { + DEB_I2C("error due to invalid start/stop condition\n"); + } + if( 0 != (status & SAA7146_I2C_DTERR) ) { + DEB_I2C("error in data transmission\n"); + } + if( 0 != (status & SAA7146_I2C_DRERR) ) { + DEB_I2C("error when receiving data\n"); + } + if( 0 != (status & SAA7146_I2C_AL) ) { + DEB_I2C("error because arbitration lost\n"); + } + + /* we handle address-errors here */ + if( 0 != (status & SAA7146_I2C_APERR) ) { + DEB_I2C("error in address phase\n"); + return -EREMOTEIO; + } + + return -EIO; + } + + /* read back data, just in case we were reading ... */ + *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); + + DEB_I2C("after: 0x%08x\n", *dword); + return 0; +} + +static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) +{ + int i = 0, count = 0; + __le32 *buffer = dev->d_i2c.cpu_addr; + int err = 0; + int short_delay = 0; + + if (mutex_lock_interruptible(&dev->i2c_lock)) + return -ERESTARTSYS; + + for(i=0;i count ) { + err = -EIO; + goto out; + } + + if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) + short_delay = 1; + + do { + /* reset the i2c-device if necessary */ + err = saa7146_i2c_reset(dev); + if ( 0 > err ) { + DEB_I2C("could not reset i2c-device\n"); + goto out; + } + + /* write out the u32s one after another */ + for(i = 0; i < count; i++) { + err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); + if ( 0 != err) { + /* this one is unsatisfying: some i2c slaves on some + dvb cards don't acknowledge correctly, so the saa7146 + thinks that an address error occurred. in that case, the + transaction should be retrying, even if an address error + occurred. analog saa7146 based cards extensively rely on + i2c address probing, however, and address errors indicate that a + device is really *not* there. retrying in that case + increases the time the device needs to probe greatly, so + it should be avoided. So we bail out in irq mode after an + address error and trust the saa7146 address error detection. */ + if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) + goto out; + DEB_I2C("error while sending message(s). starting again\n"); + break; + } + } + if( 0 == err ) { + err = num; + break; + } + + /* delay a bit before retrying */ + msleep(10); + + } while (err != num && retries--); + + /* quit if any error occurred */ + if (err != num) + goto out; + + /* if any things had to be read, get the results */ + if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) { + DEB_I2C("could not cleanup i2c-message\n"); + err = -EIO; + goto out; + } + + /* return the number of delivered messages */ + DEB_I2C("transmission successful. (msg:%d)\n", err); +out: + /* another bug in revision 0: the i2c-registers get uploaded randomly by other + uploads, so we better clear them out before continuing */ + if( 0 == dev->revision ) { + __le32 zero = 0; + saa7146_i2c_reset(dev); + if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { + pr_info("revision 0 error. this should never happen\n"); + } + } + + mutex_unlock(&dev->i2c_lock); + return err; +} + +/* utility functions */ +static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) +{ + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + + /* use helper function to transfer data */ + return saa7146_i2c_transfer(dev, msg, num, adapter->retries); +} + + +/*****************************************************************************/ +/* i2c-adapter helper functions */ + +/* exported algorithm data */ +static const struct i2c_algorithm saa7146_algo = { + .master_xfer = saa7146_i2c_xfer, + .functionality = saa7146_i2c_func, +}; + +int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) +{ + DEB_EE("bitrate: 0x%08x\n", bitrate); + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24)); + + dev->i2c_bitrate = bitrate; + saa7146_i2c_reset(dev); + + if (i2c_adapter) { + i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev); + i2c_adapter->dev.parent = &dev->pci->dev; + i2c_adapter->algo = &saa7146_algo; + i2c_adapter->algo_data = NULL; + i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; + i2c_adapter->retries = SAA7146_I2C_RETRIES; + } + + return 0; +} diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c new file mode 100644 index 000000000000..2d4a05d7bc5b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "saa7146_vv.h" + +static int vbi_pixel_to_capture = 720 * 2; + +static int vbi_workaround(struct saa7146_dev *dev) +{ + struct saa7146_vv *vv = dev->vv_data; + + u32 *cpu; + dma_addr_t dma_addr; + + int count = 0; + int i; + + DECLARE_WAITQUEUE(wait, current); + + DEB_VBI("dev:%p\n", dev); + + /* once again, a bug in the saa7146: the brs acquisition + is buggy and especially the BXO-counter does not work + as specified. there is this workaround, but please + don't let me explain it. ;-) */ + + cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL); + if (NULL == cpu) + return -ENOMEM; + + /* setup some basic programming, just for the workaround */ + saa7146_write(dev, BASE_EVEN3, dma_addr); + saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture); + saa7146_write(dev, PROT_ADDR3, dma_addr+4096); + saa7146_write(dev, PITCH3, vbi_pixel_to_capture); + saa7146_write(dev, BASE_PAGE3, 0x0); + saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0)); + saa7146_write(dev, MC2, MASK_04|MASK_20); + + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* BXO = 1h, BRS to outbound */ + WRITE_RPS1(0xc000008c); + /* wait for vbi_a or vbi_b*/ + if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { + DEB_D("...using port b\n"); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B); +/* + WRITE_RPS1(CMD_PAUSE | MASK_09); +*/ + } else { + DEB_D("...using port a\n"); + WRITE_RPS1(CMD_PAUSE | MASK_10); + } + /* upload brs */ + WRITE_RPS1(CMD_UPLOAD | MASK_08); + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */ + WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19); + /* wait for brs_done */ + WRITE_RPS1(CMD_PAUSE | MASK_08); + /* upload brs */ + WRITE_RPS1(CMD_UPLOAD | MASK_08); + /* load video-dma3 NumLines3 and NumBytes3 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4)); + /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */ + WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture)); + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */ + WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start + /* wait for brs_done */ + WRITE_RPS1(CMD_PAUSE | MASK_08); + /* upload brs and video-dma3*/ + WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04); + /* load mc2 register: enable dma3 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4)); + WRITE_RPS1(MASK_20 | MASK_04); + /* generate interrupt */ + WRITE_RPS1(CMD_INTERRUPT); + /* stop rps1 */ + WRITE_RPS1(CMD_STOP); + + /* we have to do the workaround twice to be sure that + everything is ok */ + for(i = 0; i < 2; i++) { + + /* indicate to the irq handler that we do the workaround */ + saa7146_write(dev, MC2, MASK_31|MASK_15); + + saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0)); + saa7146_write(dev, MC2, MASK_04|MASK_20); + + /* enable rps1 irqs */ + SAA7146_IER_ENABLE(dev,MASK_28); + + /* prepare to wait to be woken up by the irq-handler */ + add_wait_queue(&vv->vbi_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* start rps1 to enable workaround */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + schedule(); + + DEB_VBI("brs bug workaround %d/1\n", i); + + remove_wait_queue(&vv->vbi_wq, &wait); + __set_current_state(TASK_RUNNING); + + /* disable rps1 irqs */ + SAA7146_IER_DISABLE(dev,MASK_28); + + /* stop video-dma3 */ + saa7146_write(dev, MC1, MASK_20); + + if(signal_pending(current)) { + + DEB_VBI("aborted (rps:0x%08x)\n", + saa7146_read(dev, RPS_ADDR1)); + + /* stop rps1 for sure */ + saa7146_write(dev, MC1, MASK_29); + + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); + return -EINTR; + } + } + + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); + return 0; +} + +static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + + struct saa7146_video_dma vdma3; + + int count = 0; + unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; + unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; + +/* + vdma3.base_even = 0xc8000000+2560*70; + vdma3.base_odd = 0xc8000000; + vdma3.prot_addr = 0xc8000000+2560*164; + vdma3.pitch = 2560; + vdma3.base_page = 0; + vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above! +*/ + vdma3.base_even = buf->pt[2].offset; + vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture; + vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture; + vdma3.pitch = vbi_pixel_to_capture; + vdma3.base_page = buf->pt[2].dma | ME1; + vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture; + + saa7146_write_out_dma(dev, 3, &vdma3); + + /* write beginning of rps-program */ + count = 0; + + /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */ + + /* we don't wait here for the first field anymore. this is different from the video + capture and might cause that the first buffer is only half filled (with only + one field). but since this is some sort of streaming data, this is not that negative. + but by doing this, we can use the whole engine from videobuf-dma-sg.c... */ + +/* + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait); +*/ + /* set bit 1 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4)); + WRITE_RPS1(MASK_28 | MASK_12); + + /* turn on video-dma3 */ + WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS1(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS1(MASK_04 | MASK_20); /* => values */ + + /* wait for o_fid_a/b / e_fid_a/b toggle */ + WRITE_RPS1(CMD_PAUSE | o_wait); + WRITE_RPS1(CMD_PAUSE | e_wait); + + /* generate interrupt */ + WRITE_RPS1(CMD_INTERRUPT); + + /* stop */ + WRITE_RPS1(CMD_STOP); + + /* enable rps1 irqs */ + SAA7146_IER_ENABLE(dev, MASK_28); + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); +} + +static int buffer_activate(struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + buf->vb.state = VIDEOBUF_ACTIVE; + + DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); + saa7146_set_vbi_capture(dev,buf,next); + + mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + int err = 0; + int lines, llength, size; + + lines = 16 * 2 ; /* 2 fields */ + llength = vbi_pixel_to_capture; + size = lines * llength; + + DEB_VBI("vb:%p\n", vb); + + if (0 != buf->vb.baddr && buf->vb.bsize < size) { + DEB_VBI("size mismatch\n"); + return -EINVAL; + } + + if (buf->vb.size != size) + saa7146_dma_free(dev,q,buf); + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->vb.field = field; // FIXME: check this + + saa7146_pgtable_free(dev->pci, &buf->pt[2]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); + + err = videobuf_iolock(q,&buf->vb, NULL); + if (err) + goto oops; + err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], + dma->sglist, dma->sglen); + if (0 != err) + return err; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + + return 0; + + oops: + DEB_VBI("error out\n"); + saa7146_dma_free(dev,q,buf); + + return err; +} + +static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + int llength,lines; + + lines = 16 * 2 ; /* 2 fields */ + llength = vbi_pixel_to_capture; + + *size = lines * llength; + *count = 2; + + DEB_VBI("count:%d, size:%d\n", *count, *size); + + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_VBI("vb:%p\n", vb); + saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_VBI("vb:%p\n", vb); + saa7146_dma_free(dev,q,buf); +} + +static const struct videobuf_queue_ops vbi_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +static void vbi_stop(struct saa7146_fh *fh, struct file *file) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + unsigned long flags; + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + spin_lock_irqsave(&dev->slock,flags); + + /* disable rps1 */ + saa7146_write(dev, MC1, MASK_29); + + /* disable rps1 irqs */ + SAA7146_IER_DISABLE(dev, MASK_28); + + /* shut down dma 3 transfers */ + saa7146_write(dev, MC1, MASK_20); + + if (vv->vbi_dmaq.curr) + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); + + videobuf_queue_cancel(&fh->vbi_q); + + vv->vbi_streaming = NULL; + + del_timer(&vv->vbi_dmaq.timeout); + del_timer(&vv->vbi_read_timeout); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void vbi_read_timeout(struct timer_list *t) +{ + struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); + struct file *file = vv->vbi_read_timeout_file; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + vbi_stop(fh, file); +} + +static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) +{ + DEB_VBI("dev:%p\n", dev); + + INIT_LIST_HEAD(&vv->vbi_dmaq.queue); + + timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); + vv->vbi_dmaq.dev = dev; + + init_waitqueue_head(&vv->vbi_wq); +} + +static int vbi_open(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; + + u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); + int ret = 0; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); + if (0 == ret) { + DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); + return -EBUSY; + } + + /* adjust arbitrition control for video dma 3 */ + arbtr_ctrl &= ~0x1f0000; + arbtr_ctrl |= 0x1d0000; + saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); + saa7146_write(dev, MC2, (MASK_04|MASK_20)); + + videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, // FIXME: does this really work? + sizeof(struct saa7146_buf), + file, &dev->v4l2_lock); + + vv->vbi_read_timeout.function = vbi_read_timeout; + vv->vbi_read_timeout_file = file; + + /* initialize the brs */ + if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { + saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19)); + } else { + saa7146_write(dev, BRS_CTRL, 0x00000001); + + if (0 != (ret = vbi_workaround(dev))) { + DEB_VBI("vbi workaround failed!\n"); + /* return ret;*/ + } + } + + /* upload brs register */ + saa7146_write(dev, MC2, (MASK_08|MASK_24)); + return 0; +} + +static void vbi_close(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = dev->vv_data; + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + if( fh == vv->vbi_streaming ) { + vbi_stop(fh, file); + } + saa7146_res_free(fh, RESOURCE_DMA3_BRS); +} + +static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) +{ + struct saa7146_vv *vv = dev->vv_data; + spin_lock(&dev->slock); + + if (vv->vbi_dmaq.curr) { + DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); + /* this must be += 2, one count for each field */ + vv->vbi_fieldcount+=2; + vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); + } else { + DEB_VBI("dev:%p\n", dev); + } + saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); + + spin_unlock(&dev->slock); +} + +static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + ssize_t ret = 0; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + if( NULL == vv->vbi_streaming ) { + // fixme: check if dma3 is available + // fixme: activate vbi engine here if necessary. (really?) + vv->vbi_streaming = fh; + } + + if( fh != vv->vbi_streaming ) { + DEB_VBI("open %p is already using vbi capture\n", + vv->vbi_streaming); + return -EBUSY; + } + + mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); + ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, + file->f_flags & O_NONBLOCK); +/* + printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); + printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); + printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); + printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); + printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); + printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); + printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); +*/ + return ret; +} + +const struct saa7146_use_ops saa7146_vbi_uops = { + .init = vbi_init, + .open = vbi_open, + .release = vbi_close, + .irq_done = vbi_irq_done, + .read = vbi_read, +}; diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c new file mode 100644 index 000000000000..4598a44231fa --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c @@ -0,0 +1,1286 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "saa7146_vv.h" + +static int max_memory = 32; + +module_param(max_memory, int, 0644); +MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); + +#define IS_CAPTURE_ACTIVE(fh) \ + (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) + +#define IS_OVERLAY_ACTIVE(fh) \ + (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) + +/* format descriptions for capture and preview */ +static struct saa7146_format formats[] = { + { + .pixelformat = V4L2_PIX_FMT_RGB332, + .trans = RGB08_COMPOSED, + .depth = 8, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565, + .trans = RGB16_COMPOSED, + .depth = 16, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_BGR24, + .trans = RGB24_COMPOSED, + .depth = 24, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_BGR32, + .trans = RGB32_COMPOSED, + .depth = 32, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_RGB32, + .trans = RGB32_COMPOSED, + .depth = 32, + .flags = 0, + .swap = 0x2, + }, { + .pixelformat = V4L2_PIX_FMT_GREY, + .trans = Y8, + .depth = 8, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_YUV422P, + .trans = YUV422_DECOMPOSED, + .depth = 16, + .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420, + .trans = YUV420_DECOMPOSED, + .depth = 12, + .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420, + .trans = YUV420_DECOMPOSED, + .depth = 12, + .flags = FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_UYVY, + .trans = YUV422_COMPOSED, + .depth = 16, + .flags = 0, + } +}; + +/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. + due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped + (like V4L2_PIX_FMT_YUYV) ... 8-( */ + +struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixelformat == fourcc) { + return formats+i; + } + } + + DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f); + +int saa7146_start_preview(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct v4l2_format fmt; + int ret = 0, err = 0; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + /* check if we have overlay information */ + if (vv->ov.fh == NULL) { + DEB_D("no overlay data available. try S_FMT first.\n"); + return -EAGAIN; + } + + /* check if streaming capture is running */ + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_D("streaming capture is active\n"); + return -EBUSY; + } + + /* check if overlay is running */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + if (vv->video_fh == fh) { + DEB_D("overlay is already active\n"); + return 0; + } + DEB_D("overlay is already active in another open\n"); + return -EBUSY; + } + + if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { + DEB_D("cannot get necessary overlay resources\n"); + return -EBUSY; + } + + fmt.fmt.win = vv->ov.win; + err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); + if (0 != err) { + saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + return -EBUSY; + } + vv->ov.win = fmt.fmt.win; + + DEB_D("%dx%d+%d+%d 0x%08x field=%s\n", + vv->ov.win.w.width, vv->ov.win.w.height, + vv->ov.win.w.left, vv->ov.win.w.top, + vv->ov_fmt->pixelformat, v4l2_field_names[vv->ov.win.field]); + + if (0 != (ret = saa7146_enable_overlay(fh))) { + DEB_D("enabling overlay failed: %d\n", ret); + saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + return ret; + } + + vv->video_status = STATUS_OVERLAY; + vv->video_fh = fh; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_start_preview); + +int saa7146_stop_preview(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + /* check if streaming capture is running */ + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_D("streaming capture is active\n"); + return -EBUSY; + } + + /* check if overlay is running at all */ + if ((vv->video_status & STATUS_OVERLAY) == 0) { + DEB_D("no active overlay\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_D("overlay is active, but in another open\n"); + return -EBUSY; + } + + vv->video_status = 0; + vv->video_fh = NULL; + + saa7146_disable_overlay(fh); + + saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_stop_preview); + +/********************************************************************************/ +/* common pagetable functions */ + +static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + struct pci_dev *pci = dev->pci; + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + struct scatterlist *list = dma->sglist; + int length = dma->sglen; + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); + + if( 0 != IS_PLANAR(sfmt->trans)) { + struct saa7146_pgtable *pt1 = &buf->pt[0]; + struct saa7146_pgtable *pt2 = &buf->pt[1]; + struct saa7146_pgtable *pt3 = &buf->pt[2]; + __le32 *ptr1, *ptr2, *ptr3; + __le32 fill; + + int size = buf->fmt->width*buf->fmt->height; + int i,p,m1,m2,m3,o1,o2; + + switch( sfmt->depth ) { + case 12: { + /* create some offsets inside the page table */ + m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; + m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; + m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; + o1 = size%PAGE_SIZE; + o2 = (size+(size/4))%PAGE_SIZE; + DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", + size, m1, m2, m3, o1, o2); + break; + } + case 16: { + /* create some offsets inside the page table */ + m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; + m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; + m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; + o1 = size%PAGE_SIZE; + o2 = (size+(size/2))%PAGE_SIZE; + DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", + size, m1, m2, m3, o1, o2); + break; + } + default: { + return -1; + } + } + + ptr1 = pt1->cpu; + ptr2 = pt2->cpu; + ptr3 = pt3->cpu; + + /* walk all pages, copy all page addresses to ptr1 */ + for (i = 0; i < length; i++, list++) { + for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr1++) + *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); + } +/* + ptr1 = pt1->cpu; + for(j=0;j<40;j++) { + printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); + } +*/ + + /* if we have a user buffer, the first page may not be + aligned to a page boundary. */ + pt1->offset = dma->sglist->offset; + pt2->offset = pt1->offset+o1; + pt3->offset = pt1->offset+o2; + + /* create video-dma2 page table */ + ptr1 = pt1->cpu; + for(i = m1; i <= m2 ; i++, ptr2++) { + *ptr2 = ptr1[i]; + } + fill = *(ptr2-1); + for(;i<1024;i++,ptr2++) { + *ptr2 = fill; + } + /* create video-dma3 page table */ + ptr1 = pt1->cpu; + for(i = m2; i <= m3; i++,ptr3++) { + *ptr3 = ptr1[i]; + } + fill = *(ptr3-1); + for(;i<1024;i++,ptr3++) { + *ptr3 = fill; + } + /* finally: finish up video-dma1 page table */ + ptr1 = pt1->cpu+m1; + fill = pt1->cpu[m1]; + for(i=m1;i<1024;i++,ptr1++) { + *ptr1 = fill; + } +/* + ptr1 = pt1->cpu; + ptr2 = pt2->cpu; + ptr3 = pt3->cpu; + for(j=0;j<40;j++) { + printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); + } + for(j=0;j<40;j++) { + printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); + } + for(j=0;j<40;j++) { + printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); + } +*/ + } else { + struct saa7146_pgtable *pt = &buf->pt[0]; + return saa7146_pgtable_build_single(pci, pt, list, length); + } + + return 0; +} + + +/********************************************************************************/ +/* file operations */ + +static int video_begin(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt = NULL; + unsigned int resource; + int ret = 0, err = 0; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + if ((vv->video_status & STATUS_CAPTURE) != 0) { + if (vv->video_fh == fh) { + DEB_S("already capturing\n"); + return 0; + } + DEB_S("already capturing in another open\n"); + return -EBUSY; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { + DEB_S("warning: suspending overlay video for streaming capture\n"); + vv->ov_suspend = vv->video_fh; + err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ + if (0 != err) { + DEB_D("suspending video failed. aborting\n"); + return err; + } + } + + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); + /* we need to have a valid format set here */ + if (!fmt) + return -EINVAL; + + if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { + resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; + } else { + resource = RESOURCE_DMA1_HPS; + } + + ret = saa7146_res_get(fh, resource); + if (0 == ret) { + DEB_S("cannot get capture resource %d\n", resource); + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + return -EBUSY; + } + + /* clear out beginning of streaming bit (rps register 0)*/ + saa7146_write(dev, MC2, MASK_27 ); + + /* enable rps0 irqs */ + SAA7146_IER_ENABLE(dev, MASK_27); + + vv->video_fh = fh; + vv->video_status = STATUS_CAPTURE; + + return 0; +} + +static int video_end(struct saa7146_fh *fh, struct file *file) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_dmaqueue *q = &vv->video_dmaq; + struct saa7146_format *fmt = NULL; + unsigned long flags; + unsigned int resource; + u32 dmas = 0; + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { + DEB_S("not capturing\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_S("capturing, but in another open\n"); + return -EBUSY; + } + + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); + /* we need to have a valid format set here */ + if (!fmt) + return -EINVAL; + + if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { + resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; + dmas = MASK_22 | MASK_21 | MASK_20; + } else { + resource = RESOURCE_DMA1_HPS; + dmas = MASK_22; + } + spin_lock_irqsave(&dev->slock,flags); + + /* disable rps0 */ + saa7146_write(dev, MC1, MASK_28); + + /* disable rps0 irqs */ + SAA7146_IER_DISABLE(dev, MASK_27); + + /* shut down all used video dma transfers */ + saa7146_write(dev, MC1, dmas); + + if (q->curr) + saa7146_buffer_finish(dev, q, VIDEOBUF_DONE); + + spin_unlock_irqrestore(&dev->slock, flags); + + vv->video_fh = NULL; + vv->video_status = 0; + + saa7146_res_free(fh, resource); + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return 0; +} + +static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver)); + strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_DEVICE_CAPS; + cap->capabilities |= dev->ext_vv_data->capabilities; + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + *fb = vv->ov_fb; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; + return 0; +} + +static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt; + + DEB_EE("VIDIOC_S_FBUF\n"); + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + + /* planar formats are not allowed for overlay video, clipping and video dma would clash */ + if (fmt->flags & FORMAT_IS_PLANAR) + DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", + (char *)&fmt->pixelformat); + + /* check if overlay is running */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + if (vv->video_fh != fh) { + DEB_D("refusing to change framebuffer information while overlay is active in another open\n"); + return -EBUSY; + } + } + + /* ok, accept it */ + vv->ov_fb = *fb; + vv->ov_fmt = fmt; + + if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { + vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; + DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); + } + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + f->pixelformat = formats[f->index].pixelformat; + return 0; +} + +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct saa7146_vv *vv = dev->vv_data; + u32 val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + val = saa7146_read(dev, BCS_CTRL); + val &= 0x00ffffff; + val |= (ctrl->val << 24); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_CONTRAST: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xff00ffff; + val |= (ctrl->val << 16); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_SATURATION: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xffffff00; + val |= (ctrl->val << 0); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_HFLIP: + /* fixme: we can support changing VFLIP and HFLIP here... */ + if ((vv->video_status & STATUS_CAPTURE)) + return -EBUSY; + vv->hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + if ((vv->video_status & STATUS_CAPTURE)) + return -EBUSY; + vv->vflip = ctrl->val; + break; + + default: + return -EINVAL; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ + struct saa7146_fh *fh = vv->video_fh; + + saa7146_stop_preview(fh); + saa7146_start_preview(fh); + } + return 0; +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + parm->parm.capture.readbuffers = 1; + v4l2_video_std_frame_period(vv->standard->id, + &parm->parm.capture.timeperframe); + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.pix = vv->video_fmt; + return 0; +} + +static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.win = vv->ov.win; + return 0; +} + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.vbi = vv->vbi_fmt; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt; + enum v4l2_field field; + int maxw, maxh; + int calc_bpl; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); + + fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = vv->standard->h_max_out; + maxh = vv->standard->v_max_out; + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + switch (field) { + case V4L2_FIELD_ALTERNATE: + vv->last_field = V4L2_FIELD_TOP; + maxh = maxh / 2; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + vv->last_field = V4L2_FIELD_INTERLACED; + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + vv->last_field = V4L2_FIELD_INTERLACED; + break; + default: + DEB_D("no known field mode '%d'\n", field); + return -EINVAL; + } + + f->fmt.pix.field = field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + + calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; + + if (f->fmt.pix.bytesperline < calc_bpl) + f->fmt.pix.bytesperline = calc_bpl; + + if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ + f->fmt.pix.bytesperline = calc_bpl; + + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct v4l2_window *win = &f->fmt.win; + enum v4l2_field field; + int maxw, maxh; + + DEB_EE("dev:%p\n", dev); + + if (NULL == vv->ov_fb.base) { + DEB_D("no fb base set\n"); + return -EINVAL; + } + if (NULL == vv->ov_fmt) { + DEB_D("no fb fmt set\n"); + return -EINVAL; + } + if (win->w.width < 48 || win->w.height < 32) { + DEB_D("min width/height. (%d,%d)\n", + win->w.width, win->w.height); + return -EINVAL; + } + if (win->clipcount > 16) { + DEB_D("clipcount too big\n"); + return -EINVAL; + } + + field = win->field; + maxw = vv->standard->h_max_out; + maxh = vv->standard->v_max_out; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + DEB_D("no known field mode '%d'\n", field); + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_EE("streaming capture is active\n"); + return -EBUSY; + } + err = vidioc_try_fmt_vid_cap(file, fh, f); + if (0 != err) + return err; + vv->video_fmt = f->fmt.pix; + DEB_EE("set to pixelformat '%4.4s'\n", + (char *)&vv->video_fmt.pixelformat); + return 0; +} + +static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); + err = vidioc_try_fmt_vid_overlay(file, fh, f); + if (0 != err) + return err; + vv->ov.win = f->fmt.win; + vv->ov.nclips = f->fmt.win.clipcount; + if (vv->ov.nclips > 16) + vv->ov.nclips = 16; + memcpy(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips); + + /* vv->ov.fh is used to indicate that we have valid overlay information, too */ + vv->ov.fh = fh; + + /* check if our current overlay is active */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + saa7146_stop_preview(fh); + saa7146_start_preview(fh); + } + return 0; +} + +static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + *norm = vv->standard->id; + return 0; +} + + /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) + PAL / NTSC / SECAM. if your hardware does not (or does more) + -- override this function in your extension */ +/* + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + if (e->index < 0 ) + return -EINVAL; + if( e->index < dev->ext_vv_data->num_stds ) { + DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); + v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); + return 0; + } + return -EINVAL; + } + */ + +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + int found = 0; + int err, i; + + DEB_EE("VIDIOC_S_STD\n"); + + if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { + DEB_D("cannot change video standard while streaming capture is active\n"); + return -EBUSY; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { + vv->ov_suspend = vv->video_fh; + err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ + if (0 != err) { + DEB_D("suspending video failed. aborting\n"); + return err; + } + } + + for (i = 0; i < dev->ext_vv_data->num_stds; i++) + if (id & dev->ext_vv_data->stds[i].id) + break; + if (i != dev->ext_vv_data->num_stds) { + vv->standard = &dev->ext_vv_data->stds[i]; + if (NULL != dev->ext_vv_data->std_callback) + dev->ext_vv_data->std_callback(dev, vv->standard); + found = 1; + } + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + if (!found) { + DEB_EE("VIDIOC_S_STD: standard not found\n"); + return -EINVAL; + } + + DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name); + return 0; +} + +static int vidioc_overlay(struct file *file, void *fh, unsigned int on) +{ + int err; + + DEB_D("VIDIOC_OVERLAY on:%d\n", on); + if (on) + err = saa7146_start_preview(fh); + else + err = saa7146_stop_preview(fh); + return err; +} + +static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) +{ + struct saa7146_fh *fh = __fh; + + if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->video_q, b); + if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_reqbufs(&fh->vbi_q, b); + return -EINVAL; +} + +static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->video_q, buf); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_querybuf(&fh->vbi_q, buf); + return -EINVAL; +} + +static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->video_q, buf); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_qbuf(&fh->vbi_q, buf); + return -EINVAL; +} + +static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); + return -EINVAL; +} + +static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct saa7146_fh *fh = __fh; + int err; + + DEB_D("VIDIOC_STREAMON, type:%d\n", type); + + err = video_begin(fh); + if (err) + return err; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_streamon(&fh->video_q); + if (type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_streamon(&fh->vbi_q); + return -EINVAL; +} + +static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); + + /* ugly: we need to copy some checks from video_end(), + because videobuf_streamoff() relies on the capture running. + check and fix this */ + if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { + DEB_S("not capturing\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_S("capturing, but in another open\n"); + return -EBUSY; + } + + err = -EINVAL; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + err = videobuf_streamoff(&fh->video_q); + else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) + err = videobuf_streamoff(&fh->vbi_q); + if (0 != err) { + DEB_D("warning: videobuf_streamoff() failed\n"); + video_end(fh, file); + } else { + err = video_end(fh, file); + } + return err; +} + +const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + + .vidioc_overlay = vidioc_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/*********************************************************************************/ +/* buffer handling functions */ + +static int buffer_activate (struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + + buf->vb.state = VIDEOBUF_ACTIVE; + saa7146_set_capture(dev,buf,next); + + mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + saa7146_pgtable_free(dev->pci, &buf->pt[0]); + saa7146_pgtable_free(dev->pci, &buf->pt[1]); + saa7146_pgtable_free(dev->pci, &buf->pt[2]); +} + +static int buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + int size,err = 0; + + DEB_CAP("vbuf:%p\n", vb); + + /* sanity checks */ + if (vv->video_fmt.width < 48 || + vv->video_fmt.height < 32 || + vv->video_fmt.width > vv->standard->h_max_out || + vv->video_fmt.height > vv->standard->v_max_out) { + DEB_D("w (%d) / h (%d) out of bounds\n", + vv->video_fmt.width, vv->video_fmt.height); + return -EINVAL; + } + + size = vv->video_fmt.sizeimage; + if (0 != buf->vb.baddr && buf->vb.bsize < size) { + DEB_D("size mismatch\n"); + return -EINVAL; + } + + DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", + vv->video_fmt.width, vv->video_fmt.height, + size, v4l2_field_names[vv->video_fmt.field]); + if (buf->vb.width != vv->video_fmt.width || + buf->vb.bytesperline != vv->video_fmt.bytesperline || + buf->vb.height != vv->video_fmt.height || + buf->vb.size != size || + buf->vb.field != field || + buf->vb.field != vv->video_fmt.field || + buf->fmt != &vv->video_fmt) { + saa7146_dma_free(dev,q,buf); + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct saa7146_format *sfmt; + + buf->vb.bytesperline = vv->video_fmt.bytesperline; + buf->vb.width = vv->video_fmt.width; + buf->vb.height = vv->video_fmt.height; + buf->vb.size = size; + buf->vb.field = field; + buf->fmt = &vv->video_fmt; + buf->vb.field = vv->video_fmt.field; + + sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + release_all_pagetables(dev, buf); + if( 0 != IS_PLANAR(sfmt->trans)) { + saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); + } else { + saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); + } + + err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); + if (err) + goto oops; + err = saa7146_pgtable_build(dev,buf); + if (err) + goto oops; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + + return 0; + + oops: + DEB_D("error out\n"); + saa7146_dma_free(dev,q,buf); + + return err; +} + +static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; + + if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) + *count = MAX_SAA7146_CAPTURE_BUFFERS; + + *size = vv->video_fmt.sizeimage; + + /* check if we exceed the "max_memory" parameter */ + if( (*count * *size) > (max_memory*1048576) ) { + *count = (max_memory*1048576) / *size; + } + + DEB_CAP("%d buffers, %d bytes each\n", *count, *size); + + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_CAP("vbuf:%p\n", vb); + saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_CAP("vbuf:%p\n", vb); + + saa7146_dma_free(dev,q,buf); + + release_all_pagetables(dev, buf); +} + +static const struct videobuf_queue_ops video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************************************************************************/ +/* file operations */ + +static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) +{ + INIT_LIST_HEAD(&vv->video_dmaq.queue); + + timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); + vv->video_dmaq.dev = dev; + + /* set some default values */ + vv->standard = &dev->ext_vv_data->stds[0]; + + /* FIXME: what's this? */ + vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; + vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; +} + + +static int video_open(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + + videobuf_queue_sg_init(&fh->video_q, &video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct saa7146_buf), + file, &dev->v4l2_lock); + + return 0; +} + + +static void video_close(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = dev->vv_data; + struct videobuf_queue *q = &fh->video_q; + + if (IS_CAPTURE_ACTIVE(fh) != 0) + video_end(fh, file); + else if (IS_OVERLAY_ACTIVE(fh) != 0) + saa7146_stop_preview(fh); + + videobuf_stop(q); + /* hmm, why is this function declared void? */ +} + + +static void video_irq_done(struct saa7146_dev *dev, unsigned long st) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_dmaqueue *q = &vv->video_dmaq; + + spin_lock(&dev->slock); + DEB_CAP("called\n"); + + /* only finish the buffer if we have one... */ + if( NULL != q->curr ) { + saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); + } + saa7146_buffer_next(dev,q,0); + + spin_unlock(&dev->slock); +} + +static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + ssize_t ret = 0; + + DEB_EE("called\n"); + + if ((vv->video_status & STATUS_CAPTURE) != 0) { + /* fixme: should we allow read() captures while streaming capture? */ + if (vv->video_fh == fh) { + DEB_S("already capturing\n"); + return -EBUSY; + } + DEB_S("already capturing in another open\n"); + return -EBUSY; + } + + ret = video_begin(fh); + if( 0 != ret) { + goto out; + } + + ret = videobuf_read_one(&fh->video_q , data, count, ppos, + file->f_flags & O_NONBLOCK); + if (ret != 0) { + video_end(fh, file); + } else { + ret = video_end(fh, file); + } +out: + /* restart overlay if it was active before */ + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return ret; +} + +const struct saa7146_use_ops saa7146_video_uops = { + .init = video_init, + .open = video_open, + .release = video_close, + .irq_done = video_irq_done, + .read = video_read, +}; diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h b/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h new file mode 100644 index 000000000000..d7bd916fe3ad --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SAA7146_VV__ +#define __SAA7146_VV__ + +#include +#include +#include +#include +#include "saa7146.h" + +#define MAX_SAA7146_CAPTURE_BUFFERS 32 /* arbitrary */ +#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ + +#define WRITE_RPS0(x) do { \ + dev->d_rps0.cpu_addr[ count++ ] = cpu_to_le32(x); \ + } while (0); + +#define WRITE_RPS1(x) do { \ + dev->d_rps1.cpu_addr[ count++ ] = cpu_to_le32(x); \ + } while (0); + +struct saa7146_video_dma { + u32 base_odd; + u32 base_even; + u32 prot_addr; + u32 pitch; + u32 base_page; + u32 num_line_byte; +}; + +#define FORMAT_BYTE_SWAP 0x1 +#define FORMAT_IS_PLANAR 0x2 + +struct saa7146_format { + u32 pixelformat; + u32 trans; + u8 depth; + u8 flags; + u8 swap; +}; + +struct saa7146_standard +{ + char *name; + v4l2_std_id id; + + int v_offset; /* number of lines of vertical offset before processing */ + int v_field; /* number of lines in a field for HPS to process */ + + int h_offset; /* horizontal offset of processing window */ + int h_pixels; /* number of horizontal pixels to process */ + + int v_max_out; + int h_max_out; +}; + +/* buffer for one video/vbi frame */ +struct saa7146_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* saa7146 specific */ + struct v4l2_pix_format *fmt; + int (*activate)(struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next); + + /* page tables */ + struct saa7146_pgtable pt[3]; +}; + +struct saa7146_dmaqueue { + struct saa7146_dev *dev; + struct saa7146_buf *curr; + struct list_head queue; + struct timer_list timeout; +}; + +struct saa7146_overlay { + struct saa7146_fh *fh; + struct v4l2_window win; + struct v4l2_clip clips[16]; + int nclips; +}; + +/* per open data */ +struct saa7146_fh { + /* Must be the first field! */ + struct v4l2_fh fh; + struct saa7146_dev *dev; + + /* video capture */ + struct videobuf_queue video_q; + + /* vbi capture */ + struct videobuf_queue vbi_q; + + unsigned int resources; /* resource management for device open */ +}; + +#define STATUS_OVERLAY 0x01 +#define STATUS_CAPTURE 0x02 + +struct saa7146_vv +{ + /* vbi capture */ + struct saa7146_dmaqueue vbi_dmaq; + struct v4l2_vbi_format vbi_fmt; + struct timer_list vbi_read_timeout; + struct file *vbi_read_timeout_file; + /* vbi workaround interrupt queue */ + wait_queue_head_t vbi_wq; + int vbi_fieldcount; + struct saa7146_fh *vbi_streaming; + + int video_status; + struct saa7146_fh *video_fh; + + /* video overlay */ + struct saa7146_overlay ov; + struct v4l2_framebuffer ov_fb; + struct saa7146_format *ov_fmt; + struct saa7146_fh *ov_suspend; + + /* video capture */ + struct saa7146_dmaqueue video_dmaq; + struct v4l2_pix_format video_fmt; + enum v4l2_field last_field; + + /* common: fixme? shouldn't this be in saa7146_fh? + (this leads to a more complicated question: shall the driver + store the different settings (for example S_INPUT) for every open + and restore it appropriately, or should all settings be common for + all opens? currently, we do the latter, like all other + drivers do... */ + struct saa7146_standard *standard; + + int vflip; + int hflip; + int current_hps_source; + int current_hps_sync; + + struct saa7146_dma d_clipping; /* pointer to clipping memory */ + + unsigned int resources; /* resource management for device */ +}; + +/* flags */ +#define SAA7146_USE_PORT_B_FOR_VBI 0x2 /* use input port b for vbi hardware bug workaround */ + +struct saa7146_ext_vv +{ + /* information about the video capabilities of the device */ + int inputs; + int audios; + u32 capabilities; + int flags; + + /* additionally supported transmission standards */ + struct saa7146_standard *stds; + int num_stds; + int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *); + + /* the extension can override this */ + struct v4l2_ioctl_ops vid_ops; + struct v4l2_ioctl_ops vbi_ops; + /* pointer to the saa7146 core ops */ + const struct v4l2_ioctl_ops *core_ops; + + struct v4l2_file_operations vbi_fops; +}; + +struct saa7146_use_ops { + void (*init)(struct saa7146_dev *, struct saa7146_vv *); + int(*open)(struct saa7146_dev *, struct file *); + void (*release)(struct saa7146_dev *, struct file *); + void (*irq_done)(struct saa7146_dev *, unsigned long status); + ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); +}; + +/* from saa7146_fops.c */ +int saa7146_register_device(struct video_device *vid, struct saa7146_dev *dev, char *name, int type); +int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev); +void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state); +void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); +int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); +void saa7146_buffer_timeout(struct timer_list *t); +void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, + struct saa7146_buf *buf); + +int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv); +int saa7146_vv_release(struct saa7146_dev* dev); + +/* from saa7146_hlp.c */ +int saa7146_enable_overlay(struct saa7146_fh *fh); +void saa7146_disable_overlay(struct saa7146_fh *fh); + +void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next); +void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) ; +void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sync); +void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); + +/* from saa7146_video.c */ +extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; +extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; +extern const struct saa7146_use_ops saa7146_video_uops; +int saa7146_start_preview(struct saa7146_fh *fh); +int saa7146_stop_preview(struct saa7146_fh *fh); +long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); + +/* from saa7146_vbi.c */ +extern const struct saa7146_use_ops saa7146_vbi_uops; + +/* resource management functions */ +int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit); +void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits); + +#define RESOURCE_DMA1_HPS 0x1 +#define RESOURCE_DMA2_CLP 0x2 +#define RESOURCE_DMA3_BRS 0x4 + +/* saa7146 source inputs */ +#define SAA7146_HPS_SOURCE_PORT_A 0x00 +#define SAA7146_HPS_SOURCE_PORT_B 0x01 +#define SAA7146_HPS_SOURCE_YPB_CPA 0x02 +#define SAA7146_HPS_SOURCE_YPA_CPB 0x03 + +/* sync inputs */ +#define SAA7146_HPS_SYNC_PORT_A 0x00 +#define SAA7146_HPS_SYNC_PORT_B 0x01 + +/* some memory sizes */ +/* max. 16 clipping rectangles */ +#define SAA7146_CLIPPING_MEM (16 * 4 * sizeof(u32)) + +/* some defines for the various clipping-modes */ +#define SAA7146_CLIPPING_RECT 0x4 +#define SAA7146_CLIPPING_RECT_INVERTED 0x5 +#define SAA7146_CLIPPING_MASK 0x6 +#define SAA7146_CLIPPING_MASK_INVERTED 0x7 + +/* output formats: each entry holds four information */ +#define RGB08_COMPOSED 0x0217 /* composed is used in the sense of "not-planar" */ +/* this means: planar?=0, yuv2rgb-conversation-mode=2, dither=yes(=1), format-mode = 7 */ +#define RGB15_COMPOSED 0x0213 +#define RGB16_COMPOSED 0x0210 +#define RGB24_COMPOSED 0x0201 +#define RGB32_COMPOSED 0x0202 + +#define Y8 0x0006 +#define YUV411_COMPOSED 0x0003 +#define YUV422_COMPOSED 0x0000 +/* this means: planar?=1, yuv2rgb-conversion-mode=0, dither=no(=0), format-mode = b */ +#define YUV411_DECOMPOSED 0x100b +#define YUV422_DECOMPOSED 0x1009 +#define YUV420_DECOMPOSED 0x100a + +#define IS_PLANAR(x) (x & 0xf000) + +/* misc defines */ +#define SAA7146_NO_SWAP (0x0) +#define SAA7146_TWO_BYTE_SWAP (0x1) +#define SAA7146_FOUR_BYTE_SWAP (0x2) + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig b/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig new file mode 100644 index 000000000000..228e8d3f8d2b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_HEXIUM_GEMINI + tristate "Hexium Gemini frame grabber (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + help + This is a video4linux driver for the Hexium Gemini frame + grabber card by Hexium. Please note that the Gemini Dual + card is *not* fully supported. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called hexium_gemini. + +config VIDEO_HEXIUM_ORION + tristate "Hexium HV-PCI6 and Orion frame grabber (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + help + This is a video4linux driver for the Hexium HV-PCI6 and + Orion frame grabber cards by Hexium. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called hexium_orion. + +config VIDEO_MXB + tristate "Siemens-Nixdorf 'Multimedia eXtension Board' (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + select VIDEO_TUNER + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT + help + This is a video4linux driver for the 'Multimedia eXtension Board' + TV card by Siemens-Nixdorf. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called mxb. diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/Makefile b/drivers/staging/media/deprecated/saa7146/saa7146/Makefile new file mode 100644 index 000000000000..37c9336f83d5 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_MXB) += mxb.o +obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o +obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o + +ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/TODO b/drivers/staging/media/deprecated/saa7146/saa7146/TODO new file mode 100644 index 000000000000..c9ae2ec79cea --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/TODO @@ -0,0 +1,7 @@ +The saa7146-based drivers are one of the few drivers still not using +the vb2 framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c new file mode 100644 index 000000000000..124e82bd4507 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards + + Visit http://www.mihu.de/linux/saa7146/ and follow the link + to "hexium" for further details about this card. + + Copyright (C) 2003 Michael Hunold + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include +#include +#include "../common/saa7146_vv.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug verbosity"); + +/* global variables */ +static int hexium_num; + +#define HEXIUM_GEMINI 4 +#define HEXIUM_GEMINI_DUAL 5 + +#define HEXIUM_INPUTS 9 +static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +#define HEXIUM_AUDIOS 0 + +struct hexium_data +{ + s8 adr; + u8 byte; +}; + +#define HEXIUM_GEMINI_V_1_0 1 +#define HEXIUM_GEMINI_DUAL_V_1_0 2 + +struct hexium +{ + int type; + + struct video_device video_dev; + struct i2c_adapter i2c_adapter; + + int cur_input; /* current input */ + v4l2_std_id cur_std; /* current standard */ +}; + +/* Samsung KS0127B decoder default registers */ +static u8 hexium_ks0127b[0x100]={ +/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10, +/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06, +/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00, +/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22, +/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00, +/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80, +/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00, +/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static struct hexium_data hexium_pal[] = { + { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_ntsc[] = { + { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_secam[] = { + { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_input_select[] = { + { 0x02, 0x60 }, + { 0x02, 0x64 }, + { 0x02, 0x61 }, + { 0x02, 0x65 }, + { 0x02, 0x62 }, + { 0x02, 0x66 }, + { 0x02, 0x68 }, + { 0x02, 0x69 }, + { 0x02, 0x6A }, +}; + +/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which + are currently *not* supported*/ +static struct saa7146_standard hexium_standards[] = { + { + .name = "PAL", .id = V4L2_STD_PAL, + .v_offset = 28, .v_field = 288, + .h_offset = 1, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 28, .v_field = 240, + .h_offset = 1, .h_pixels = 640, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 28, .v_field = 288, + .h_offset = 1, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int hexium_init_done(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + union i2c_smbus_data data; + int i = 0; + + DEB_D("hexium_init_done called\n"); + + /* initialize the helper ics to useful values */ + for (i = 0; i < sizeof(hexium_ks0127b); i++) { + data.byte = hexium_ks0127b[i]; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("hexium_init_done() failed for address 0x%02x\n", + i); + } + } + + return 0; +} + +static int hexium_set_input(struct hexium *hexium, int input) +{ + union i2c_smbus_data data; + + DEB_D("\n"); + + data.byte = hexium_input_select[input].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) { + return -1; + } + + return 0; +} + +static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) +{ + union i2c_smbus_data data; + int i = 0; + + DEB_D("\n"); + + while (vdec[i].adr != -1) { + data.byte = vdec[i].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", + i); + return -1; + } + i++; + } + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + + if (i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D("VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("VIDIOC_S_INPUT %d\n", input); + + if (input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + return 0; +} + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct hexium *hexium; + int ret; + + DEB_EE("\n"); + + hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); + if (!hexium) + return -ENOMEM; + + dev->ext_priv = hexium; + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); + + strscpy(hexium->i2c_adapter.name, "hexium gemini", + sizeof(hexium->i2c_adapter.name)); + saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(hexium); + return -EFAULT; + } + + /* set HWControl GPIO number 2 */ + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + saa7146_write(dev, DD1_INIT, 0x07000700); + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* the rest */ + hexium->cur_input = 0; + hexium_init_done(dev); + + hexium_set_standard(hexium, hexium_pal); + hexium->cur_std = V4L2_STD_PAL; + + hexium_set_input(hexium, 0); + hexium->cur_input = 0; + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return ret; + } + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO); + if (ret < 0) { + pr_err("cannot register capture v4l2 device. skipping.\n"); + saa7146_vv_release(dev); + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return ret; + } + + pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num); + hexium_num++; + + return 0; +} + +static int hexium_detach(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + saa7146_unregister_device(&hexium->video_dev, dev); + saa7146_vv_release(dev); + + hexium_num--; + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + if (V4L2_STD_PAL == std->id) { + hexium_set_standard(hexium, hexium_pal); + hexium->cur_std = V4L2_STD_PAL; + return 0; + } else if (V4L2_STD_NTSC == std->id) { + hexium_set_standard(hexium, hexium_ntsc); + hexium->cur_std = V4L2_STD_NTSC; + return 0; + } else if (V4L2_STD_SECAM == std->id) { + hexium_set_standard(hexium, hexium_secam); + hexium->cur_std = V4L2_STD_SECAM; + return 0; + } + + return -1; +} + +static struct saa7146_extension hexium_extension; + +static struct saa7146_pci_extension_data hexium_gemini_4bnc = { + .ext_priv = "Hexium Gemini (4 BNC)", + .ext = &hexium_extension, +}; + +static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { + .ext_priv = "Hexium Gemini Dual (4 BNC)", + .ext = &hexium_extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2401, + .driver_data = (unsigned long) &hexium_gemini_4bnc, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2402, + .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, + }, + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = HEXIUM_INPUTS, + .capabilities = 0, + .stds = &hexium_standards[0], + .num_stds = ARRAY_SIZE(hexium_standards), + .std_callback = &std_callback, +}; + +static struct saa7146_extension hexium_extension = { + .name = "hexium gemini", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .attach = hexium_attach, + .detach = hexium_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init hexium_init_module(void) +{ + if (0 != saa7146_register_extension(&hexium_extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit hexium_cleanup_module(void) +{ + saa7146_unregister_extension(&hexium_extension); +} + +module_init(hexium_init_module); +module_exit(hexium_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards"); +MODULE_AUTHOR("Michael Hunold "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c new file mode 100644 index 000000000000..ebd63998ac79 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards + + Visit http://www.mihu.de/linux/saa7146/ and follow the link + to "hexium" for further details about this card. + + Copyright (C) 2003 Michael Hunold + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include +#include +#include "../common/saa7146_vv.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug verbosity"); + +/* global variables */ +static int hexium_num; + +#define HEXIUM_HV_PCI6_ORION 1 +#define HEXIUM_ORION_1SVHS_3BNC 2 +#define HEXIUM_ORION_4BNC 3 + +#define HEXIUM_INPUTS 9 +static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +#define HEXIUM_AUDIOS 0 + +struct hexium_data +{ + s8 adr; + u8 byte; +}; + +struct hexium +{ + int type; + struct video_device video_dev; + struct i2c_adapter i2c_adapter; + + int cur_input; /* current input */ +}; + +/* Philips SAA7110 decoder default registers */ +static u8 hexium_saa7110[53]={ +/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00, +/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90, +/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA, +/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00, +/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F, +/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03, +/*30*/ 0x44,0x75,0x01,0x8C,0x03 +}; + +static struct { + struct hexium_data data[8]; +} hexium_input_select[] = { +{ + { /* cvbs 1 */ + { 0x06, 0x00 }, + { 0x20, 0xD9 }, + { 0x21, 0x17 }, // 0x16, + { 0x22, 0x40 }, + { 0x2C, 0x03 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, // ?? + { 0x21, 0x16 }, // 0x03, + } +}, { + { /* cvbs 2 */ + { 0x06, 0x00 }, + { 0x20, 0x78 }, + { 0x21, 0x07 }, // 0x03, + { 0x22, 0xD2 }, + { 0x2C, 0x83 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ? + { 0x21, 0x03 }, + } +}, { + { /* cvbs 3 */ + { 0x06, 0x00 }, + { 0x20, 0xBA }, + { 0x21, 0x07 }, // 0x05, + { 0x22, 0x91 }, + { 0x2C, 0x03 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x05 }, // 0x03, + } +}, { + { /* cvbs 4 */ + { 0x06, 0x00 }, + { 0x20, 0xD8 }, + { 0x21, 0x17 }, // 0x16, + { 0x22, 0x40 }, + { 0x2C, 0x03 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, // ?? + { 0x21, 0x16 }, // 0x03, + } +}, { + { /* cvbs 5 */ + { 0x06, 0x00 }, + { 0x20, 0xB8 }, + { 0x21, 0x07 }, // 0x05, + { 0x22, 0x91 }, + { 0x2C, 0x03 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x05 }, // 0x03, + } +}, { + { /* cvbs 6 */ + { 0x06, 0x00 }, + { 0x20, 0x7C }, + { 0x21, 0x07 }, // 0x03 + { 0x22, 0xD2 }, + { 0x2C, 0x83 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x03 }, + } +}, { + { /* y/c 1 */ + { 0x06, 0x80 }, + { 0x20, 0x59 }, + { 0x21, 0x17 }, + { 0x22, 0x42 }, + { 0x2C, 0xA3 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, + { 0x21, 0x12 }, + } +}, { + { /* y/c 2 */ + { 0x06, 0x80 }, + { 0x20, 0x9A }, + { 0x21, 0x17 }, + { 0x22, 0xB1 }, + { 0x2C, 0x13 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, + { 0x21, 0x14 }, + } +}, { + { /* y/c 3 */ + { 0x06, 0x80 }, + { 0x20, 0x3C }, + { 0x21, 0x27 }, + { 0x22, 0xC1 }, + { 0x2C, 0x23 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, + { 0x21, 0x21 }, + } +} +}; + +static struct saa7146_standard hexium_standards[] = { + { + .name = "PAL", .id = V4L2_STD_PAL, + .v_offset = 16, .v_field = 288, + .h_offset = 1, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 16, .v_field = 240, + .h_offset = 1, .h_pixels = 640, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 16, .v_field = 288, + .h_offset = 1, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +/* this is only called for old HV-PCI6/Orion cards + without eeprom */ +static int hexium_probe(struct saa7146_dev *dev) +{ + struct hexium *hexium = NULL; + union i2c_smbus_data data; + int err = 0; + + DEB_EE("\n"); + + /* there are no hexium orion cards with revision 0 saa7146s */ + if (0 == dev->revision) { + return -EFAULT; + } + + hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); + if (!hexium) + return -ENOMEM; + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); + + saa7146_write(dev, DD1_INIT, 0x01000100); + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + strscpy(hexium->i2c_adapter.name, "hexium orion", + sizeof(hexium->i2c_adapter.name)); + saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(hexium); + return -EFAULT; + } + + /* set SAA7110 control GPIO 0 */ + saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI); + /* set HWControl GPIO number 2 */ + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + mdelay(10); + + /* detect newer Hexium Orion cards by subsystem ids */ + if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) { + pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_ORION_1SVHS_3BNC; + return 0; + } + + if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) { + pr_info("device is a Hexium Orion w/ 4 BNC inputs\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_ORION_4BNC; + return 0; + } + + /* check if this is an old hexium Orion card by looking at + a saa7110 at address 0x4e */ + err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, + 0x00, I2C_SMBUS_BYTE_DATA, &data); + if (err == 0) { + pr_info("device is a Hexium HV-PCI6/Orion (old)\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_HV_PCI6_ORION; + return 0; + } + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return -EFAULT; +} + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int hexium_init_done(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + union i2c_smbus_data data; + int i = 0; + + DEB_D("hexium_init_done called\n"); + + /* initialize the helper ics to useful values */ + for (i = 0; i < sizeof(hexium_saa7110); i++) { + data.byte = hexium_saa7110[i]; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("failed for address 0x%02x\n", i); + } + } + + return 0; +} + +static int hexium_set_input(struct hexium *hexium, int input) +{ + union i2c_smbus_data data; + int i = 0; + + DEB_D("\n"); + + for (i = 0; i < 8; i++) { + int adr = hexium_input_select[input].data[i].adr; + data.byte = hexium_input_select[input].data[i].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) { + return -1; + } + pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte); + } + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + + if (i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D("VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + if (input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + + return 0; +} + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + int ret; + + DEB_EE("\n"); + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + pr_err("Error in saa7146_vv_init()\n"); + return ret; + } + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) { + pr_err("cannot register capture v4l2 device. skipping.\n"); + return -1; + } + + pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num); + hexium_num++; + + /* the rest */ + hexium->cur_input = 0; + hexium_init_done(dev); + + return 0; +} + +static int hexium_detach(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + saa7146_unregister_device(&hexium->video_dev, dev); + saa7146_vv_release(dev); + + hexium_num--; + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) +{ + return 0; +} + +static struct saa7146_extension extension; + +static struct saa7146_pci_extension_data hexium_hv_pci6 = { + .ext_priv = "Hexium HV-PCI6 / Orion", + .ext = &extension, +}; + +static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = { + .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)", + .ext = &extension, +}; + +static struct saa7146_pci_extension_data hexium_orion_4bnc = { + .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)", + .ext = &extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x0000, + .subdevice = 0x0000, + .driver_data = (unsigned long) &hexium_hv_pci6, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x0101, + .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2101, + .driver_data = (unsigned long) &hexium_orion_4bnc, + }, + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = HEXIUM_INPUTS, + .capabilities = 0, + .stds = &hexium_standards[0], + .num_stds = ARRAY_SIZE(hexium_standards), + .std_callback = &std_callback, +}; + +static struct saa7146_extension extension = { + .name = "hexium HV-PCI6 Orion", + .flags = 0, // SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .probe = hexium_probe, + .attach = hexium_attach, + .detach = hexium_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init hexium_init_module(void) +{ + if (0 != saa7146_register_extension(&extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit hexium_cleanup_module(void) +{ + saa7146_unregister_extension(&extension); +} + +module_init(hexium_init_module); +module_exit(hexium_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards"); +MODULE_AUTHOR("Michael Hunold "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c b/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c new file mode 100644 index 000000000000..3e568f952dae --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c @@ -0,0 +1,873 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + mxb - v4l2 driver for the Multimedia eXtension Board + + Copyright (C) 1998-2006 Michael Hunold + + Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html + for further details about this card. + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include +#include +#include +#include +#include + +#include "../common/saa7146_vv.h" +#include "tea6415c.h" +#include "tea6420.h" + +#define MXB_AUDIOS 6 + +#define I2C_SAA7111A 0x24 +#define I2C_TDA9840 0x42 +#define I2C_TEA6415C 0x43 +#define I2C_TEA6420_1 0x4c +#define I2C_TEA6420_2 0x4d +#define I2C_TUNER 0x60 + +#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) + +/* global variable */ +static int mxb_num; + +/* initial frequence the tuner will be tuned to. + in verden (lower saxony, germany) 4148 is a + channel called "phoenix" */ +static int freq = 4148; +module_param(freq, int, 0644); +MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); + +#define MXB_INPUTS 4 +enum { TUNER, AUX1, AUX3, AUX3_YC }; + +static struct v4l2_input mxb_inputs[MXB_INPUTS] = { + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, + V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +/* this array holds the information, which port of the saa7146 each + input actually uses. the mxb uses port 0 for every input */ +static struct { + int hps_source; + int hps_sync; +} input_port_selection[MXB_INPUTS] = { + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, +}; + +/* this array holds the information of the audio source (mxb_audios), + which has to be switched corresponding to the video source (mxb_channels) */ +static int video_audio_connect[MXB_INPUTS] = + { 0, 1, 3, 3 }; + +struct mxb_routing { + u32 input; + u32 output; +}; + +/* these are the available audio sources, which can switched + to the line- and cd-output individually */ +static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { + { + .index = 0, + .name = "Tuner", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 1, + .name = "AUX1", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 2, + .name = "AUX2", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 3, + .name = "AUX3", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 4, + .name = "Radio (X9)", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 5, + .name = "CD-ROM (X10)", + .capability = V4L2_AUDCAP_STEREO, + } +}; + +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the CD-output. Note that gain is set to 0 in this table. */ +static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { + { { 1, 1 }, { 1, 1 } }, /* Tuner */ + { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ + { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ + { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ + { { 1, 1 }, { 3, 1 } }, /* Radio */ + { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ + { { 6, 1 }, { 6, 1 } } /* Mute */ +}; + +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the line-output. Note that gain is set to 0 in this table. */ +static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { + { { 2, 3 }, { 1, 2 } }, + { { 5, 3 }, { 6, 2 } }, + { { 4, 3 }, { 6, 2 } }, + { { 3, 3 }, { 6, 2 } }, + { { 2, 3 }, { 3, 2 } }, + { { 2, 3 }, { 2, 2 } }, + { { 6, 3 }, { 6, 2 } } /* Mute */ +}; + +struct mxb +{ + struct video_device video_dev; + struct video_device vbi_dev; + + struct i2c_adapter i2c_adapter; + + struct v4l2_subdev *saa7111a; + struct v4l2_subdev *tda9840; + struct v4l2_subdev *tea6415c; + struct v4l2_subdev *tuner; + struct v4l2_subdev *tea6420_1; + struct v4l2_subdev *tea6420_2; + + int cur_mode; /* current audio mode (mono, stereo, ...) */ + int cur_input; /* current input */ + int cur_audinput; /* current audio input */ + int cur_mute; /* current mute status */ + struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ +}; + +#define saa7111a_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->saa7111a, o, f, ##args) +#define tda9840_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tda9840, o, f, ##args) +#define tea6415c_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tea6415c, o, f, ##args) +#define tuner_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tuner, o, f, ##args) +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) + +static void mxb_update_audmode(struct mxb *mxb) +{ + struct v4l2_tuner t = { + .audmode = mxb->cur_mode, + }; + + tda9840_call(mxb, tuner, s_tuner, &t); +} + +static inline void tea6420_route(struct mxb *mxb, int idx) +{ + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); +} + +static struct saa7146_extension extension; + +static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct mxb *mxb = dev->ext_priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mxb->cur_mute = ctrl->val; + /* switch the audio-source */ + tea6420_route(mxb, ctrl->val ? 6 : + video_audio_connect[mxb->cur_input]); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops mxb_ctrl_ops = { + .s_ctrl = mxb_s_ctrl, +}; + +static int mxb_probe(struct saa7146_dev *dev) +{ + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct mxb *mxb = NULL; + + v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (hdl->error) + return hdl->error; + mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); + if (mxb == NULL) { + DEB_D("not enough kernel memory\n"); + return -ENOMEM; + } + + + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); + + saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(mxb); + return -EFAULT; + } + + mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "saa7111", I2C_SAA7111A, NULL); + mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", I2C_TEA6420_1, NULL); + mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", I2C_TEA6420_2, NULL); + mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6415c", I2C_TEA6415C, NULL); + mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tda9840", I2C_TDA9840, NULL); + mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tuner", I2C_TUNER, NULL); + + /* check if all devices are present */ + if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || + !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { + pr_err("did not find all i2c devices. aborting\n"); + i2c_del_adapter(&mxb->i2c_adapter); + kfree(mxb); + return -ENODEV; + } + + /* all devices are present, probe was successful */ + + /* we store the pointer in our private data field */ + dev->ext_priv = mxb; + + v4l2_ctrl_handler_setup(hdl); + + return 0; +} + +/* some init data for the saa7740, the so-called 'sound arena module'. + there are no specs available, so we simply use some init values */ +static struct { + int length; + char data[9]; +} mxb_saa7740_init[] = { + { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, + { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, + { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, + { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, + { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, + { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, + { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, + { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, + { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, + { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, + { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, + { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, + { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, + { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, + { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, + { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, + { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, + { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, + { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, + { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, + { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, + { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, + { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, + { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, + { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, + { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, + { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, + { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, + { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, + { 3, { 0x48, 0x00, 0x01 } }, + { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, + { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, + { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, + { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, + { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, + { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, + { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, + { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, + { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, + { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, + { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, + { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, + { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, + { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, + { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, + { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, + { 3, { 0x80, 0xb3, 0x0a } }, + {-1, { 0 } } +}; + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int mxb_init_done(struct saa7146_dev* dev) +{ + struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct i2c_msg msg; + struct tuner_setup tun_setup; + v4l2_std_id std = V4L2_STD_PAL_BG; + + int i, err = 0; + + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + + /* select video mode in saa7111a */ + saa7111a_call(mxb, video, s_std, std); + + /* select tuner-output on saa7111a */ + saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, + SAA7111_FMT_CCIR, 0); + + /* select a tuner type */ + tun_setup.mode_mask = T_ANALOG_TV; + tun_setup.addr = ADDR_UNSET; + tun_setup.type = TUNER_PHILIPS_PAL; + tuner_call(mxb, tuner, s_type_addr, &tun_setup); + /* tune in some frequency on tuner */ + mxb->cur_freq.tuner = 0; + mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; + mxb->cur_freq.frequency = freq; + tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); + + /* set a default video standard */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, video, s_std, std); + tuner_call(mxb, video, s_std, std); + + /* switch to tuner-channel on tea6415c */ + tea6415c_call(mxb, video, s_routing, 3, 17, 0); + + /* select tuner-output on multicable on tea6415c */ + tea6415c_call(mxb, video, s_routing, 3, 13, 0); + + /* the rest for mxb */ + mxb->cur_input = 0; + mxb->cur_audinput = video_audio_connect[mxb->cur_input]; + mxb->cur_mute = 1; + + mxb->cur_mode = V4L2_TUNER_MODE_STEREO; + mxb_update_audmode(mxb); + + /* check if the saa7740 (aka 'sound arena module') is present + on the mxb. if so, we must initialize it. due to lack of + information about the saa7740, the values were reverse + engineered. */ + msg.addr = 0x1b; + msg.flags = 0; + msg.len = mxb_saa7740_init[0].length; + msg.buf = &mxb_saa7740_init[0].data[0]; + + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err == 1) { + /* the sound arena module is a pos, that's probably the reason + philips refuses to hand out a datasheet for the saa7740... + it seems to screw up the i2c bus, so we disable fast irq + based i2c transactions here and rely on the slow and safe + polling method ... */ + extension.flags &= ~SAA7146_USE_I2C_IRQ; + for (i = 1; ; i++) { + if (-1 == mxb_saa7740_init[i].length) + break; + + msg.len = mxb_saa7740_init[i].length; + msg.buf = &mxb_saa7740_init[i].data[0]; + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err != 1) { + DEB_D("failed to initialize 'sound arena module'\n"); + goto err; + } + } + pr_info("'sound arena module' detected\n"); + } +err: + /* the rest for saa7146: you should definitely set some basic values + for the input-port handling of the saa7146. */ + + /* ext->saa has been filled by the core driver */ + + /* some stuff is done via variables */ + saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, + input_port_selection[mxb->cur_input].hps_sync); + + /* some stuff is done via direct write to the registers */ + + /* this is ugly, but because of the fact that this is completely + hardware dependend, it should be done directly... */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x02000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + return 0; +} + +/* interrupt-handler. this gets called when irq_mask is != 0. + it must clear the interrupt-bits in irq_mask it has handled */ +/* +void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) +{ + struct mxb* mxb = (struct mxb*)dev->ext_priv; +} +*/ + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + if (i->index >= MXB_INPUTS) + return -EINVAL; + memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + *i = mxb->cur_input; + + DEB_EE("VIDIOC_G_INPUT %d\n", *i); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + int err = 0; + int i = 0; + + DEB_EE("VIDIOC_S_INPUT %d\n", input); + + if (input >= MXB_INPUTS) + return -EINVAL; + + mxb->cur_input = input; + + saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, + input_port_selection[input].hps_sync); + + /* prepare switching of tea6415c and saa7111a; + have a look at the 'background'-file for further information */ + switch (input) { + case TUNER: + i = SAA7115_COMPOSITE0; + + err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); + + /* connect tuner-output always to multicable */ + if (!err) + err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); + break; + case AUX3_YC: + /* nothing to be done here. aux3_yc is + directly connected to the saa711a */ + i = SAA7115_SVIDEO1; + break; + case AUX3: + /* nothing to be done here. aux3 is + directly connected to the saa711a */ + i = SAA7115_COMPOSITE1; + break; + case AUX1: + i = SAA7115_COMPOSITE0; + err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); + break; + } + + if (err) + return err; + + /* switch video in saa7111a */ + if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) + pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); + + mxb->cur_audinput = video_audio_connect[input]; + /* switch the audio-source only if necessary */ + if (0 == mxb->cur_mute) + tea6420_route(mxb, mxb->cur_audinput); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (t->index) { + DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", + t->index); + return -EINVAL; + } + + DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); + + memset(t, 0, sizeof(*t)); + strscpy(t->name, "TV Tuner", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->audmode = mxb->cur_mode; + return call_all(dev, tuner, g_tuner, t); +} + +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (t->index) { + DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", + t->index); + return -EINVAL; + } + + mxb->cur_mode = t->audmode; + return call_all(dev, tuner, s_tuner, t); +} + +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + return call_all(dev, video, querystd, norm); +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (f->tuner) + return -EINVAL; + *f = mxb->cur_freq; + + DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + struct saa7146_vv *vv = dev->vv_data; + + if (f->tuner) + return -EINVAL; + + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + + DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); + + /* tune in desired frequency */ + tuner_call(mxb, tuner, s_frequency, f); + /* let the tuner subdev clamp the frequency to the tuner range */ + mxb->cur_freq = *f; + tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + if (mxb->cur_input) + return 0; + + /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ + spin_lock(&dev->slock); + vv->vbi_fieldcount = 0; + spin_unlock(&dev->slock); + + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (a->index >= MXB_AUDIOS) + return -EINVAL; + *a = mxb_audios[a->index]; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_EE("VIDIOC_G_AUDIO\n"); + *a = mxb_audios[mxb->cur_audinput]; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); + if (a->index >= 32 || + !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) + return -EINVAL; + + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + if (reg->reg > pci_resource_len(dev->pci, 0) - 4) + return -EINVAL; + reg->val = saa7146_read(dev, reg->reg); + reg->size = 4; + return 0; +} + +static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + if (reg->reg > pci_resource_len(dev->pci, 0) - 4) + return -EINVAL; + saa7146_write(dev, reg->reg, reg->val); + return 0; +} +#endif + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct mxb *mxb; + int ret; + + DEB_EE("dev:%p\n", dev); + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + ERR("Error in saa7146_vv_init()"); + return ret; + } + + if (mxb_probe(dev)) { + saa7146_vv_release(dev); + return -1; + } + mxb = (struct mxb *)dev->ext_priv; + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_querystd = vidioc_querystd; + vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vv_data.vid_ops.vidioc_g_register = vidioc_g_register; + vv_data.vid_ops.vidioc_s_register = vidioc_s_register; +#endif + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { + ERR("cannot register capture v4l2 device. skipping.\n"); + saa7146_vv_release(dev); + return -1; + } + + /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ + if (MXB_BOARD_CAN_DO_VBI(dev)) { + if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { + ERR("cannot register vbi v4l2 device. skipping.\n"); + } + } + + pr_info("found Multimedia eXtension Board #%d\n", mxb_num); + + mxb_num++; + mxb_init_done(dev); + return 0; +} + +static int mxb_detach(struct saa7146_dev *dev) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + + saa7146_unregister_device(&mxb->video_dev,dev); + if (MXB_BOARD_CAN_DO_VBI(dev)) + saa7146_unregister_device(&mxb->vbi_dev, dev); + saa7146_vv_release(dev); + + mxb_num--; + + i2c_del_adapter(&mxb->i2c_adapter); + kfree(mxb); + + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (V4L2_STD_PAL_I == standard->id) { + v4l2_std_id std = V4L2_STD_PAL_I; + + DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 0); + saa7111a_call(mxb, video, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, video, s_std, std); + } else { + v4l2_std_id std = V4L2_STD_PAL_BG; + + if (mxb->cur_input) + std = standard->id; + DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, video, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, video, s_std, std); + } + return 0; +} + +static struct saa7146_standard standard[] = { + { + .name = "PAL-BG", .id = V4L2_STD_PAL_BG, + .v_offset = 0x17, .v_field = 288, + .h_offset = 0x14, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "PAL-I", .id = V4L2_STD_PAL_I, + .v_offset = 0x17, .v_field = 288, + .h_offset = 0x14, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x16, .v_field = 240, + .h_offset = 0x06, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 0x14, .v_field = 288, + .h_offset = 0x14, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +static struct saa7146_pci_extension_data mxb = { + .ext_priv = "Multimedia eXtension Board", + .ext = &extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x0000, + .subdevice = 0x0000, + .driver_data = (unsigned long)&mxb, + }, { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = MXB_INPUTS, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, +}; + +static struct saa7146_extension extension = { + .name = "Multimedia eXtension Board", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .attach = mxb_attach, + .detach = mxb_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init mxb_init_module(void) +{ + if (saa7146_register_extension(&extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit mxb_cleanup_module(void) +{ + saa7146_unregister_extension(&extension); +} + +module_init(mxb_init_module); +module_exit(mxb_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); +MODULE_AUTHOR("Michael Hunold "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig b/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig new file mode 100644 index 000000000000..8c85ed58e938 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_BUDGET_CORE + tristate "SAA7146 DVB cards (aka Budget, Nova-PCI) (DEPRECATED)" + depends on DVB_CORE && PCI && I2C + select VIDEO_SAA7146 + select TTPCI_EEPROM + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder. + +config DVB_BUDGET + tristate "Budget cards (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + help + Support for simple SAA7146 based DVB cards (so called Budget- + or Nova-PCI cards) without onboard MPEG2 decoder, and without + analog inputs or an onboard Common Interface connector. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget. + +config DVB_BUDGET_CI + tristate "Budget cards with onboard CI connector (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + depends on RC_CORE + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with onboard Common Interface connector. + + Note: The Common Interface is not yet supported by this driver + due to lack of information from the vendor. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-ci. + +config DVB_BUDGET_AV + tristate "Budget cards with analog video inputs (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select VIDEO_SAA7146_VV + depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with one or more analog video inputs. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-av. diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/Makefile b/drivers/staging/media/deprecated/saa7146/ttpci/Makefile new file mode 100644 index 000000000000..b0708f6e40cc --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the kernel SAA7146 FULL TS DVB device driver +# + +obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o +obj-$(CONFIG_DVB_BUDGET) += budget.o +obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o +obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o + +ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ +ccflags-y += -I $(srctree)/drivers/media/tuners +ccflags-y += -I $(srctree)/drivers/media/common diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/TODO b/drivers/staging/media/deprecated/saa7146/ttpci/TODO new file mode 100644 index 000000000000..c9ae2ec79cea --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/TODO @@ -0,0 +1,7 @@ +The saa7146-based drivers are one of the few drivers still not using +the vb2 framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c new file mode 100644 index 000000000000..0c61a2dec221 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c @@ -0,0 +1,1622 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-av.c: driver for the SAA7146 based Budget DVB cards + * with analog video in + * + * Compiled from various sources by Michael Hunold + * + * CI interface support (c) 2004 Olivier Gournet & + * Andrew de Quincey + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * the project's page is at https://linuxtv.org + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "budget.h" +#include "stv0299.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "tda8261.h" +#include "tda8261_cfg.h" +#include "tda1002x.h" +#include "tda1004x.h" +#include "tua6100.h" +#include "dvb-pll.h" +#include "../common/saa7146_vv.h" +#include +#include +#include +#include +#include +#include + +#include + +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_av { + struct budget budget; + struct video_device vd; + int cur_input; + int has_saa7113; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + struct dvb_ca_en50221 ca; + u8 reinitialise_demod:1; +}; + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); + + +/* GPIO Connections: + * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! + * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory + * 2 - CI Card Enable (Active Low) + * 3 - CI Card Detect + */ + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + +static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) +{ + u8 mm1[] = { 0x00 }; + u8 mm2[] = { 0x00 }; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = id / 2; + mm1[0] = reg; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; + + i2c_transfer(i2c, msgs, 2); + + return mm2[0]; +} + +static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) +{ + u8 mm1[] = { reg }; + struct i2c_msg msgs[2] = { + {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, + {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} + }; + + if (i2c_transfer(i2c, msgs, 2) != 2) + return -EIO; + + return 0; +} + +static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) +{ + u8 msg[2] = { reg, val }; + struct i2c_msg msgs; + + msgs.flags = 0; + msgs.addr = id / 2; + msgs.len = 2; + msgs.buf = msg; + return i2c_transfer(i2c, &msgs, 1); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 1\n"); + } + return result; +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 2\n"); + } + return result; +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 3\n"); + return -ETIMEDOUT; + } + return result; +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + } + return result; +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_reset\n"); + budget_av->slot_status = SLOTSTATUS_RESET; + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ + msleep(2); + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ + msleep(20); /* 20 ms Vcc settling time */ + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + msleep(20); + + /* reinitialise the frontend if necessary */ + if (budget_av->reinitialise_demod) + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_shutdown\n"); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + budget_av->slot_status = SLOTSTATUS_NONE; + + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + + return 0; +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + if (slot != 0) + return -EINVAL; + + /* test the card detect line - needs to be done carefully + * since it never goes high for some CAMs on this interface (e.g. topuptv) */ + if (budget_av->slot_status == SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + udelay(1); + if (saa7146_read(saa, PSR) & MASK_06) { + if (budget_av->slot_status == SLOTSTATUS_NONE) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted A\n"); + } + } + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } + + /* We also try and read from IO memory to work round the above detection bug. If + * there is no CAM, we will get a timeout. Only done if there is no cam + * present, since this test actually breaks some cams :( + * + * if the CI interface is not open, we also do the above test since we + * don't care if the cam has problems - we'll be resetting it on open() anyway */ + if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); + if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted B\n"); + } else if (result < 0) { + if (budget_av->slot_status != SLOTSTATUS_NONE) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + return 0; + } + } + } + + /* read from attribute memory in reset/ready state to know when the CAM is ready */ + if (budget_av->slot_status == SLOTSTATUS_RESET) { + result = ciintf_read_attribute_mem(ca, slot, 0); + if (result == 0x1d) { + budget_av->slot_status = SLOTSTATUS_READY; + } + } + + /* work out correct return code */ + if (budget_av->slot_status != SLOTSTATUS_NONE) { + if (budget_av->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + return 0; +} + +static int ciintf_init(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + + /* Enable DEBI pins */ + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + /* register CI interface */ + budget_av->ca.owner = THIS_MODULE; + budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_av->ca.read_cam_control = ciintf_read_cam_control; + budget_av->ca.write_cam_control = ciintf_write_cam_control; + budget_av->ca.slot_reset = ciintf_slot_reset; + budget_av->ca.slot_shutdown = ciintf_slot_shutdown; + budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_av->ca.poll_slot_status = ciintf_poll_slot_status; + budget_av->ca.data = budget_av; + budget_av->budget.ci_present = 1; + budget_av->slot_status = SLOTSTATUS_NONE; + + if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, + &budget_av->ca, 0, 1)) != 0) { + pr_err("ci initialisation failed\n"); + goto error; + } + + pr_info("ci interface initialised\n"); + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + + /* release the CA device */ + dvb_ca_en50221_release(&budget_av->ca); + + /* disable DEBI pins */ + saa7146_write(saa, MC1, MASK_27); +} + + +static const u8 saa7113_tab[] = { + 0x01, 0x08, + 0x02, 0xc0, + 0x03, 0x33, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xeb, + 0x07, 0xe0, + 0x08, 0x28, + 0x09, 0x00, + 0x0a, 0x80, + 0x0b, 0x47, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x44, + + 0x10, 0x08, + 0x11, 0x0c, + 0x12, 0x7b, + 0x13, 0x00, + 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + + 0x57, 0xff, + 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, + 0x5b, 0x83, 0x5e, 0x00, + 0xff +}; + +static int saa7113_init(struct budget_av *budget_av) +{ + struct budget *budget = &budget_av->budget; + struct saa7146_dev *saa = budget->dev; + const u8 *data = saa7113_tab; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); + msleep(200); + + if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { + dprintk(1, "saa7113 not found on KNC card\n"); + return -ENODEV; + } + + dprintk(1, "saa7113 detected and initializing\n"); + + while (*data != 0xff) { + i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); + data += 2; + } + + dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); + + return 0; +} + +static int saa7113_setinput(struct budget_av *budget_av, int input) +{ + struct budget *budget = &budget_av->budget; + + if (1 != budget_av->has_saa7113) + return -ENODEV; + + if (input == 1) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); + } else if (input == 0) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); + } else + return -EINVAL; + + budget_av->cur_input = input; + return 0; +} + + +static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 div; + u8 buf[4]; + struct budget *budget = (struct budget *) fe->dvb->priv; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((c->frequency < 950000) || (c->frequency > 2150000)) + return -EINVAL; + + div = (c->frequency + (125 - 1)) / 125; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0x20; + + if (c->symbol_rate < 4000000) + buf[3] |= 1; + + if (c->frequency < 1250000) + buf[3] |= 0; + else if (c->frequency < 1550000) + buf[3] |= 0x40; + else if (c->frequency < 2050000) + buf[3] |= 0x80; + else if (c->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static u8 typhoon_cinergy1200s_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x92, + 0xff, 0xff +}; + +static const struct stv0299_config typhoon_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + + +static const struct stv0299_config cinergy_1200s_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static const struct stv0299_config cinergy_1200s_1894_0010_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 buf[6]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + int i; + +#define CU1216_IF 36125000 +#define TUNER_MUL 62500 + + u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0xce; + buf[3] = (c->frequency < 150000000 ? 0x01 : + c->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda1002x_config philips_cu1216_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static struct tda1002x_config philips_cu1216_config_altaddress = { + .demod_address = 0x0d, + .invert = 0, +}; + +static struct tda10023_config philips_cu1216_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static int philips_tu1216_tuner_init(struct dvb_frontend *fe) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + return 0; +} + +static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = + sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = c->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (c->frequency < 49000000) + return -EINVAL; + else if (c->frequency < 161000000) + band = 1; + else if (c->frequency < 444000000) + band = 2; + else if (c->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter + switch (c->bandwidth_hz) { + case 6000000: + filter = 0; + break; + + case 7000000: + filter = 0; + break; + + case 8000000: + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; + + // setup tuner buffer + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tu1216_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + +static struct tda1004x_config philips_tu1216_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 1, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tu1216_request_firmware, +}; + +static u8 philips_sd1878_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x35, + 0x06, 0x40, + 0x07, 0x00, + 0x08, 0x43, + 0x09, 0x02, + 0x0C, 0x51, + 0x0D, 0x82, + 0x0E, 0x23, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static const struct stv0299_config philips_sd1878_config = { + .demod_address = 0x68, + .inittab = philips_sd1878_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, +}; + +/* KNC1 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x08 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x33 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xee }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +/* STB0899 demodulator config for the KNC1 and clones */ +static struct stb0899_config knc1_dvbs2_config = { + .init_dev = knc1_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = knc1_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, +// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ + .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ +// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_OFF, + + .lo_clk = 76500000, + .hi_clk = 90000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = tda8261_get_frequency, + .tuner_set_frequency = tda8261_set_frequency, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = tda8261_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +/* + * SD1878/SHA tuner config + * 1F, Single I/P, Horizontal mount, High Sensitivity + */ +static const struct tda8261_config sd1878c_config = { +// .name = "SD1878/SHA", + .addr = 0x60, + .step_size = TDA8261_STEP_1000 /* kHz */ +}; + +static u8 read_pwm(struct budget_av *budget_av) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, + {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} + }; + + if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +#define SUBID_DVBS_KNC1 0x0010 +#define SUBID_DVBS_KNC1_PLUS 0x0011 +#define SUBID_DVBS_TYPHOON 0x4f56 +#define SUBID_DVBS_CINERGY1200 0x1154 +#define SUBID_DVBS_CYNERGY1200N 0x1155 +#define SUBID_DVBS_TV_STAR 0x0014 +#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 +#define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS2_KNC1 0x0018 +#define SUBID_DVBS2_KNC1_OEM 0x0019 +#define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH_2 0x001b +#define SUBID_DVBS2_EASYWATCH 0x001d +#define SUBID_DVBS_EASYWATCH 0x001e + +#define SUBID_DVBC_EASYWATCH 0x002a +#define SUBID_DVBC_EASYWATCH_MK3 0x002c +#define SUBID_DVBC_KNC1 0x0020 +#define SUBID_DVBC_KNC1_PLUS 0x0021 +#define SUBID_DVBC_KNC1_MK3 0x0022 +#define SUBID_DVBC_KNC1_TDA10024 0x0028 +#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +#define SUBID_DVBC_CINERGY1200 0x1156 +#define SUBID_DVBC_CINERGY1200_MK3 0x1176 + +#define SUBID_DVBT_EASYWATCH 0x003a +#define SUBID_DVBT_KNC1_PLUS 0x0031 +#define SUBID_DVBT_KNC1 0x0030 +#define SUBID_DVBT_CINERGY1200 0x1157 + +static void frontend_init(struct budget_av *budget_av) +{ + struct saa7146_dev * saa = budget_av->budget.dev; + struct dvb_frontend * fe = NULL; + + /* Enable / PowerON Frontend */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + + /* Wait for PowerON */ + msleep(100); + + /* additional setup necessary for the PLUS cards */ + switch (saa->pci->subsystem_device) { + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBC_EASYWATCH: + case SUBID_DVBC_KNC1_PLUS_MK3: + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); + break; + } + + switch (saa->pci->subsystem_device) { + + case SUBID_DVBS_KNC1: + /* + * maybe that setting is needed for other dvb-s cards as well, + * but so far it has been only confirmed for this type + */ + budget_av->reinitialise_demod = 1; + fallthrough; + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBS_EASYWATCH_1: + if (saa->pci->subsystem_vendor == 0x1894) { + fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); + } + } else { + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + } + break; + + case SUBID_DVBS_TV_STAR: + case SUBID_DVBS_TV_STAR_PLUS_X4: + case SUBID_DVBS_TV_STAR_CI: + case SUBID_DVBS_CYNERGY1200N: + case SUBID_DVBS_EASYWATCH: + case SUBID_DVBS_EASYWATCH_2: + fe = dvb_attach(stv0299_attach, &philips_sd1878_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(dvb_pll_attach, fe, 0x60, + &budget_av->budget.i2c_adap, + DVB_PLL_PHILIPS_SD1878_TDA8261); + } + break; + + case SUBID_DVBS_TYPHOON: + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + budget_av->reinitialise_demod = 1; + if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) + dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); + + break; + case SUBID_DVBS_CINERGY1200: + fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + + case SUBID_DVBC_KNC1: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBC_CINERGY1200: + case SUBID_DVBC_EASYWATCH: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10021_attach, &philips_cu1216_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe == NULL) + fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBC_EASYWATCH_MK3: + case SUBID_DVBC_CINERGY1200_MK3: + case SUBID_DVBC_KNC1_MK3: + case SUBID_DVBC_KNC1_TDA10024: + case SUBID_DVBC_KNC1_PLUS_MK3: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10023_attach, + &philips_cu1216_tda10023_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBT_EASYWATCH: + case SUBID_DVBT_KNC1: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBT_CINERGY1200: + budget_av->reinitialise_demod = 1; + fe = dvb_attach(tda10046_attach, &philips_tu1216_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.init = philips_tu1216_tuner_init; + fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; + } + break; + } + + if (fe == NULL) { + pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + saa->pci->vendor, + saa->pci->device, + saa->pci->subsystem_vendor, + saa->pci->subsystem_device); + return; + } + + budget_av->budget.dvb_frontend = fe; + + if (dvb_register_frontend(&budget_av->budget.dvb_adapter, + budget_av->budget.dvb_frontend)) { + pr_err("Frontend registration failed!\n"); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + budget_av->budget.dvb_frontend = NULL; + } +} + + +static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); +} + +static int budget_av_detach(struct saa7146_dev *dev) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (1 == budget_av->has_saa7113) { + saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); + + msleep(200); + + saa7146_unregister_device(&budget_av->vd, dev); + + saa7146_vv_release(dev); + } + + if (budget_av->budget.ci_present) + ciintf_deinit(budget_av); + + if (budget_av->budget.dvb_frontend != NULL) { + dvb_unregister_frontend(budget_av->budget.dvb_frontend); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_av->budget); + + kfree(budget_av); + + return err; +} + +#define KNC1_INPUTS 2 +static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { + { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, +}; + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); + if (i->index >= KNC1_INPUTS) + return -EINVAL; + memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + *i = budget_av->cur_input; + + dprintk(1, "VIDIOC_G_INPUT %d\n", *i); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + dprintk(1, "VIDIOC_S_INPUT %d\n", input); + return saa7113_setinput(budget_av, input); +} + +static struct saa7146_ext_vv vv_data; + +static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_av *budget_av; + u8 *mac; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) + return -ENOMEM; + + budget_av->has_saa7113 = 0; + budget_av->budget.ci_present = 0; + + dev->ext_priv = budget_av; + + err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) { + kfree(budget_av); + return err; + } + + /* knc1 initialization */ + saa7146_write(dev, DD1_STREAM_B, 0x04000000); + saa7146_write(dev, DD1_INIT, 0x07000600); + saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); + + if (saa7113_init(budget_av) == 0) { + budget_av->has_saa7113 = 1; + err = saa7146_vv_init(dev, &vv_data); + if (err != 0) { + /* fixme: proper cleanup here */ + ERR("cannot init vv subsystem\n"); + return err; + } + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) { + /* fixme: proper cleanup here */ + ERR("cannot register capture v4l2 device\n"); + saa7146_vv_release(dev); + return err; + } + + /* beware: this modifies dev->vv ... */ + saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, + SAA7146_HPS_SYNC_PORT_A); + + saa7113_setinput(budget_av, 0); + } + + /* fixme: find some sane values here... */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + mac = budget_av->budget.dvb_adapter.proposed_mac; + if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { + pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", + budget_av->budget.dvb_adapter.num); + eth_zero_addr(mac); + } else { + pr_info("KNC1-%d: MAC addr = %pM\n", + budget_av->budget.dvb_adapter.num, mac); + } + + budget_av->budget.dvb_adapter.priv = budget_av; + frontend_init(budget_av); + ciintf_init(budget_av); + + ttpci_budget_init_hooks(&budget_av->budget); + + return 0; +} + +static struct saa7146_standard standard[] = { + {.name = "PAL",.id = V4L2_STD_PAL, + .v_offset = 0x17,.v_field = 288, + .h_offset = 0x14,.h_pixels = 680, + .v_max_out = 576,.h_max_out = 768 }, + + {.name = "NTSC",.id = V4L2_STD_NTSC, + .v_offset = 0x16,.v_field = 240, + .h_offset = 0x06,.h_pixels = 708, + .v_max_out = 480,.h_max_out = 640, }, +}; + +static struct saa7146_ext_vv vv_data = { + .inputs = 2, + .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 + .flags = 0, + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), +}; + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); +MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); +MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); +MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); +MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); +MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), + MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), + MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), + MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), + MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), + MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), + MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), + MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), + MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), + MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), + MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), + MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), + MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), + MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), + MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), + MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), + MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), + MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), + MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), + MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), + MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), + MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_av", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = pci_tbl, + + .module = THIS_MODULE, + .attach = budget_av_attach, + .detach = budget_av_detach, + + .irq_mask = MASK_10, + .irq_func = budget_av_irq, +}; + +static int __init budget_av_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_av_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_av_init); +module_exit(budget_av_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c new file mode 100644 index 000000000000..d59d18647371 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c @@ -0,0 +1,1574 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-ci.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * msp430 IR support contributed by Jack Thomasson + * partially based on the Siemens DVB driver by Ralph+Marcus Metzler + * + * CI interface support (c) 2004 Andrew de Quincey + * + * the project's page is at https://linuxtv.org + */ + +#include +#include +#include +#include +#include +#include + +#include "budget.h" + +#include +#include "stv0299.h" +#include "stv0297.h" +#include "tda1004x.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "lnbp21.h" +#include "bsbe1.h" +#include "bsru6.h" +#include "tda1002x.h" +#include "tda827x.h" +#include "bsbe1-d01a.h" + +#define MODULE_NAME "budget_ci" + +/* + * Regarding DEBIADDR_IR: + * Some CI modules hang if random addresses are read. + * Using address 0x4000 for the IR read means that we + * use the same address as for CI version, which should + * be a safe default. + */ +#define DEBIADDR_IR 0x4000 +#define DEBIADDR_CICONTROL 0x0000 +#define DEBIADDR_CIVERSION 0x4000 +#define DEBIADDR_IO 0x1000 +#define DEBIADDR_ATTR 0x3000 + +#define CICONTROL_RESET 0x01 +#define CICONTROL_ENABLETS 0x02 +#define CICONTROL_CAMDETECT 0x08 + +#define DEBICICTL 0x00420000 +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +/* RC5 device wildcard */ +#define IR_DEVICE_ANY 255 + +static int rc5_device = -1; +module_param(rc5_device, int, 0644); +MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); + +static int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_ci_ir { + struct rc_dev *dev; + struct tasklet_struct msp430_irq_tasklet; + char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ + char phys[32]; + int rc5_device; + u32 ir_key; + bool have_command; + bool full_rc5; /* Outputs a full RC5 code */ +}; + +struct budget_ci { + struct budget budget; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + int ci_irq; + struct dvb_ca_en50221 ca; + struct budget_ci_ir ir; + u8 tuner_pll_address; /* used for philips_tdm1316l configs */ +}; + +static void msp430_ir_interrupt(struct tasklet_struct *t) +{ + struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); + struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); + struct rc_dev *dev = budget_ci->ir.dev; + u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + + /* + * The msp430 chip can generate two different bytes, command and device + * + * type1: X1CCCCCC, C = command bits (0 - 63) + * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit + * + * Each signal from the remote control can generate one or more command + * bytes and one or more device bytes. For the repeated bytes, the + * highest bit (X) is set. The first command byte is always generated + * before the first device byte. Other than that, no specific order + * seems to apply. To make life interesting, bytes can also be lost. + * + * Only when we have a command and device byte, a keypress is + * generated. + */ + + if (ir_debug) + printk("budget_ci: received byte 0x%02x\n", command); + + /* Remove repeat bit, we use every command */ + command = command & 0x7f; + + /* Is this a RC5 command byte? */ + if (command & 0x40) { + budget_ci->ir.have_command = true; + budget_ci->ir.ir_key = command & 0x3f; + return; + } + + /* It's a RC5 device byte */ + if (!budget_ci->ir.have_command) + return; + budget_ci->ir.have_command = false; + + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && + budget_ci->ir.rc5_device != (command & 0x1f)) + return; + + if (budget_ci->ir.full_rc5) { + rc_keydown(dev, RC_PROTO_RC5, + RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key), + !!(command & 0x20)); + return; + } + + /* FIXME: We should generate complete scancodes for all devices */ + rc_keydown(dev, RC_PROTO_UNKNOWN, budget_ci->ir.ir_key, + !!(command & 0x20)); +} + +static int msp430_ir_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + struct rc_dev *dev; + int error; + + dev = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!dev) { + printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); + return -ENOMEM; + } + + snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), + "Budget-CI dvb ir receiver %s", saa->name); + snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), + "pci-%s/ir0", pci_name(saa->pci)); + + dev->driver_name = MODULE_NAME; + dev->device_name = budget_ci->ir.name; + dev->input_phys = budget_ci->ir.phys; + dev->input_id.bustype = BUS_PCI; + dev->input_id.version = 1; + if (saa->pci->subsystem_vendor) { + dev->input_id.vendor = saa->pci->subsystem_vendor; + dev->input_id.product = saa->pci->subsystem_device; + } else { + dev->input_id.vendor = saa->pci->vendor; + dev->input_id.product = saa->pci->device; + } + dev->dev.parent = &saa->pci->dev; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + + /* Select keymap and address */ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: + case 0x100f: + case 0x1011: + case 0x1012: + /* The hauppauge keymap is a superset of these remotes */ + dev->map_name = RC_MAP_HAUPPAUGE; + budget_ci->ir.full_rc5 = true; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = 0x1f; + break; + case 0x1010: + case 0x1017: + case 0x1019: + case 0x101a: + case 0x101b: + /* for the Technotrend 1500 bundled remote */ + dev->map_name = RC_MAP_TT_1500; + break; + default: + /* unknown remote */ + dev->map_name = RC_MAP_BUDGET_CI_OLD; + break; + } + if (!budget_ci->ir.full_rc5) + dev->scancode_mask = 0xff; + + error = rc_register_device(dev); + if (error) { + printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); + rc_free_device(dev); + return error; + } + + budget_ci->ir.dev = dev; + + tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); + + SAA7146_IER_ENABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); + + return 0; +} + +static void msp430_ir_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + SAA7146_IER_DISABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + + rc_unregister_device(budget_ci->ir.dev); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, 1, 0); +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, value, 1, 0); +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + if (budget_ci->ci_irq) { + // trigger on RISING edge during reset so we know when READY is re-asserted + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + budget_ci->slot_status = SLOTSTATUS_RESET; + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + int tmp; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + + tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + tmp | CICONTROL_ENABLETS, 1, 0); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + return 0; +} + +static void ciintf_interrupt(struct tasklet_struct *t) +{ + struct budget_ci *budget_ci = from_tasklet(budget_ci, t, + ciintf_irq_tasklet); + struct saa7146_dev *saa = budget_ci->budget.dev; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + + // GPIO should be set to trigger on falling edge if a CAM is present + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + // CAM insertion IRQ + budget_ci->slot_status = SLOTSTATUS_PRESENT; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); + + } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { + // CAM ready (reset completed) + budget_ci->slot_status = SLOTSTATUS_READY; + dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); + + } else if (budget_ci->slot_status & SLOTSTATUS_READY) { + // FR/DA IRQ + dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); + } + } else { + + // trigger on rising edge if a CAM is not present - when a CAM is inserted, we + // only want to get the IRQ when it sets READY. If we trigger on the falling edge, + // the CAM might not actually be ready yet. + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + + // generate a CAM removal IRQ if we haven't already + if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { + // CAM removal IRQ + budget_ci->slot_status = SLOTSTATUS_NONE; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); + } + } +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return -EINVAL; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + // mark it as present if it wasn't before + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + budget_ci->slot_status = SLOTSTATUS_PRESENT; + } + + // during a RESET, we check if we can read from IO memory to see when CAM is ready + if (budget_ci->slot_status & SLOTSTATUS_RESET) { + if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { + budget_ci->slot_status = SLOTSTATUS_READY; + } + } + } else { + budget_ci->slot_status = SLOTSTATUS_NONE; + } + + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + if (budget_ci->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + + return 0; +} + +static int ciintf_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + int flags; + int result; + int ci_version; + int ca_flags; + + memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); + + // enable DEBI pins + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + // test if it is there + ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); + if ((ci_version & 0xa0) != 0xa0) { + result = -ENODEV; + goto error; + } + + // determine whether a CAM is present or not + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + budget_ci->slot_status = SLOTSTATUS_NONE; + if (flags & CICONTROL_CAMDETECT) + budget_ci->slot_status = SLOTSTATUS_PRESENT; + + // version 0xa2 of the CI firmware doesn't generate interrupts + if (ci_version == 0xa2) { + ca_flags = 0; + budget_ci->ci_irq = 0; + } else { + ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | + DVB_CA_EN50221_FLAG_IRQ_FR | + DVB_CA_EN50221_FLAG_IRQ_DA; + budget_ci->ci_irq = 1; + } + + // register CI interface + budget_ci->ca.owner = THIS_MODULE; + budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_ci->ca.read_cam_control = ciintf_read_cam_control; + budget_ci->ca.write_cam_control = ciintf_write_cam_control; + budget_ci->ca.slot_reset = ciintf_slot_reset; + budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; + budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; + budget_ci->ca.data = budget_ci; + if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, + &budget_ci->ca, + ca_flags, 1)) != 0) { + printk("budget_ci: CI interface detected, but initialisation failed.\n"); + goto error; + } + + // Setup CI slot IRQ + if (budget_ci->ci_irq) { + tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + } else { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + SAA7146_IER_ENABLE(saa, MASK_03); + } + + // enable interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // success! + printk("budget_ci: CI interface initialised\n"); + budget_ci->budget.ci_present = 1; + + // forge a fake CI IRQ so the CAM state is setup correctly + if (budget_ci->ci_irq) { + flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; + if (budget_ci->slot_status != SLOTSTATUS_NONE) + flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); + } + + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + // disable CI interrupts + if (budget_ci->ci_irq) { + SAA7146_IER_DISABLE(saa, MASK_03); + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ciintf_irq_tasklet); + } + + // reset interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // disable TS data stream to CI interface + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + + // release the CA device + dvb_ca_en50221_release(&budget_ci->ca); + + // disable DEBI pins + saa7146_write(saa, MC1, MASK_27); +} + +static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); + + if (*isr & MASK_06) + tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); + + if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) + tasklet_schedule(&budget_ci->ciintf_irq_tasklet); +} + +static u8 philips_su1278_tt_inittab[] = { + 0x01, 0x0f, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x5b, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x02, + 0x09, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0f, 0x14, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x97, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + stv0299_writereg(fe, 0x0e, 0x44); + if (srate >= 10000000) { + stv0299_writereg(fe, 0x13, 0x97); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x17, 0x8c); + stv0299_writereg(fe, 0x1a, 0xfe); + stv0299_writereg(fe, 0x1c, 0x7f); + stv0299_writereg(fe, 0x2d, 0x09); + } else { + stv0299_writereg(fe, 0x13, 0x99); + stv0299_writereg(fe, 0x14, 0x8d); + stv0299_writereg(fe, 0x15, 0xce); + stv0299_writereg(fe, 0x17, 0x43); + stv0299_writereg(fe, 0x1a, 0x1d); + stv0299_writereg(fe, 0x1c, 0x12); + stv0299_writereg(fe, 0x2d, 0x05); + } + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x15, 0xc9); + + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u32 div; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = (p->frequency + (500 - 1)) / 500; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; + buf[3] = 0x20; + + if (p->symbol_rate < 4000000) + buf[3] |= 1; + + if (p->frequency < 1250000) + buf[3] |= 0; + else if (p->frequency < 1550000) + buf[3] |= 0x40; + else if (p->frequency < 2050000) + buf[3] |= 0x80; + else if (p->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static const struct stv0299_config philips_su1278_tt_config = { + + .demod_address = 0x68, + .inittab = philips_su1278_tt_inittab, + .mclk = 64000000UL, + .invert = 0, + .skip_reinit = 1, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 50, + .set_symbol_rate = philips_su1278_tt_set_symbol_rate, +}; + + + +static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = + sizeof(td1316_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36130000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (p->frequency < 49000000) + return -EINVAL; + else if (p->frequency < 159000000) + band = 1; + else if (p->frequency < 444000000) + band = 2; + else if (p->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter and TDA9889 + switch (p->bandwidth_hz) { + case 6000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 0; + break; + + case 7000000: + tda1004x_writereg(fe, 0x0C, 0x80); + filter = 0; + break; + + case 8000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + + return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 0, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static struct tda1004x_config philips_tdm1316l_config_invert = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[5]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, + .flags = 0, + .buf = tuner_buf, + .len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36125000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) { + cp = 3; + band = 1; + } else if (tuner_frequency < 160000000) { + cp = 5; + band = 1; + } else if (tuner_frequency < 200000000) { + cp = 6; + band = 1; + } else if (tuner_frequency < 290000000) { + cp = 3; + band = 2; + } else if (tuner_frequency < 420000000) { + cp = 5; + band = 2; + } else if (tuner_frequency < 480000000) { + cp = 6; + band = 2; + } else if (tuner_frequency < 620000000) { + cp = 3; + band = 4; + } else if (tuner_frequency < 830000000) { + cp = 5; + band = 4; + } else if (tuner_frequency < 895000000) { + cp = 7; + band = 4; + } else + return -EINVAL; + + // assume PLL filter should always be 8MHz for the moment. + filter = 1; + + // calculate divisor + tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xc8; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + tuner_buf[4] = 0x80; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(50); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + + return 0; +} + +static u8 dvbc_philips_tdm1316l_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x20, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0x7b, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x38, + 0x62, 0x0a, + 0x53, 0x13, + 0x59, 0x08, + 0xff, 0xff, +}; + +static struct stv0297_config dvbc_philips_tdm1316l_config = { + .demod_address = 0x1c, + .inittab = dvbc_philips_tdm1316l_inittab, + .invert = 0, + .stop_during_read = 1, +}; + +static struct tda10023_config tda10023_config = { + .demod_address = 0xc, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xa511, +}; + +static struct tda827x_config tda827x_config = { + .config = 0, +}; + +/* TT S2-3200 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xc7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +static struct stb0899_config tt3200_config = { + .init_dev = tt3200_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = tt3200_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +static struct stb6100_config tt3200_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + +static void frontend_init(struct budget_ci *budget_ci) +{ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + break; + } + break; + + case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; + break; + } + break; + + case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x61; + budget_ci->budget.dvb_frontend = + dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) + budget_ci->tuner_pll_address = 0x63; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x60; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1017: // TT S-1500 PCI + budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + + budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ + budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { + printk(KERN_ERR "%s: No tda827x found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ + budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + printk(KERN_ERR "%s: No STB6000 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x1019: // TT S2-3200 PCI + /* + * NOTE! on some STB0899 versions, the internal PLL takes a longer time + * to settle, aka LOCK. On the older revisions of the chip, we don't see + * this, as a result on the newer chips the entire clock tree, will not + * be stable after a freshly POWER 'ed up situation. + * In this case, we should RESET the STB0899 (Active LOW) and wait for + * PLL stabilization. + * + * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is + * connected to the SAA7146 GPIO, GPIO2, Pin 142 + */ + /* Reset Demodulator */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); + /* Wait for everything to die */ + msleep(50); + /* Pull it up out of Reset state */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); + /* Wait for PLL to stabilize */ + msleep(250); + /* + * PLL state should be stable now. Ideally, we should check + * for PLL LOCK status. But well, never mind! + */ + budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + } + + if (budget_ci->budget.dvb_frontend == NULL) { + printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget_ci->budget.dev->pci->vendor, + budget_ci->budget.dev->pci->device, + budget_ci->budget.dev->pci->subsystem_vendor, + budget_ci->budget.dev->pci->subsystem_device); + } else { + if (dvb_register_frontend + (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { + printk("budget-ci: Frontend registration failed!\n"); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } +} + +static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_ci *budget_ci; + int err; + + budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); + if (!budget_ci) { + err = -ENOMEM; + goto out1; + } + + dprintk(2, "budget_ci: %p\n", budget_ci); + + dev->ext_priv = budget_ci; + + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) + goto out2; + + err = msp430_ir_init(budget_ci); + if (err) + goto out3; + + ciintf_init(budget_ci); + + budget_ci->budget.dvb_adapter.priv = budget_ci; + frontend_init(budget_ci); + + ttpci_budget_init_hooks(&budget_ci->budget); + + return 0; + +out3: + ttpci_budget_deinit(&budget_ci->budget); +out2: + kfree(budget_ci); +out1: + return err; +} + +static int budget_ci_detach(struct saa7146_dev *dev) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + struct saa7146_dev *saa = budget_ci->budget.dev; + int err; + + if (budget_ci->budget.ci_present) + ciintf_deinit(budget_ci); + msp430_ir_deinit(budget_ci); + if (budget_ci->budget.dvb_frontend) { + dvb_unregister_frontend(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_ci->budget); + + // disable frontend and CI interface + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + + kfree(budget_ci); + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), + MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), + MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), + MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), + MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), + MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), + MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), + MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_ci dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = &pci_tbl[0], + .attach = budget_ci_attach, + .detach = budget_ci_detach, + + .irq_mask = MASK_03 | MASK_06 | MASK_10, + .irq_func = budget_ci_irq, +}; + +static int __init budget_ci_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_ci_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_ci_init); +module_exit(budget_ci_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards w/ CI-module produced by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c new file mode 100644 index 000000000000..5d5796f24469 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-core.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher , + * Oliver Endriss , + * Andreas 'randy' Weinberger + * + * the project's page is at https://linuxtv.org + */ + + +#include "budget.h" +#include "ttpci-eeprom.h" + +#define TS_WIDTH (2 * TS_SIZE) +#define TS_WIDTH_ACTIVY TS_SIZE +#define TS_WIDTH_DVBC TS_SIZE +#define TS_HEIGHT_MASK 0xf00 +#define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_HEIGHT_MASK_DVBC 0xe00 +#define TS_MIN_BUFSIZE_K 188 +#define TS_MAX_BUFSIZE_K 1410 +#define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define TS_MAX_BUFSIZE_K_DVBC 1316 +#define BUFFER_WARNING_WAIT (30*HZ) + +int budget_debug; +static int dma_buffer_size = TS_MIN_BUFSIZE_K; +module_param_named(debug, budget_debug, int, 0644); +module_param_named(bufsize, dma_buffer_size, int, 0444); +MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); + +/**************************************************************************** + * TT budget / WinTV Nova + ****************************************************************************/ + +static int stop_ts_capture(struct budget *budget) +{ + dprintk(2, "budget: %p\n", budget); + + saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off + SAA7146_IER_DISABLE(budget->dev, MASK_10); + return 0; +} + +static int start_ts_capture(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + if (!budget->feeding || !budget->fe_synced) + return 0; + + saa7146_write(dev, MC1, MASK_20); // DMA3 off + + memset(budget->grabbing, 0x00, budget->buffer_size); + + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + + budget->ttbp = 0; + + /* + * Signal path on the Activy: + * + * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory + * + * Since the tuner feeds 204 bytes packets into the SAA7146, + * DMA3 is configured to strip the trailing 16 FEC bytes: + * Pitch: 188, NumBytes3: 188, NumLines3: 1024 + */ + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + saa7146_write(dev, DD1_INIT, 0x04000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + break; + case BUDGET_PATCH: + saa7146_write(dev, DD1_INIT, 0x00000200); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + break; + case BUDGET_CIN1200C_MK3: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + break; + default: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x02000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + } + + saa7146_write(dev, MC2, (MASK_08 | MASK_24)); + mdelay(10); + + saa7146_write(dev, BASE_ODD3, 0); + if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { + // using odd/even buffers + saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); + } else { + // using a single buffer + saa7146_write(dev, BASE_EVEN3, 0); + } + saa7146_write(dev, PROT_ADDR3, budget->buffer_size); + saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); + + saa7146_write(dev, PITCH3, budget->buffer_width); + saa7146_write(dev, NUM_LINE_BYTE3, + (budget->buffer_height << 16) | budget->buffer_width); + + saa7146_write(dev, MC2, (MASK_04 | MASK_20)); + + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ + SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ + saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ + + return 0; +} + +static int budget_read_fe_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + int synced; + int ret; + + if (budget->read_fe_status) + ret = budget->read_fe_status(fe, status); + else + ret = -EINVAL; + + if (!ret) { + synced = (*status & FE_HAS_LOCK); + if (synced != budget->fe_synced) { + budget->fe_synced = synced; + spin_lock(&budget->feedlock); + if (synced) + start_ts_capture(budget); + else + stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + } + } + return ret; +} + +static void vpeirq(struct tasklet_struct *t) +{ + struct budget *budget = from_tasklet(budget, t, vpe_tasklet); + u8 *mem = (u8 *) (budget->grabbing); + u32 olddma = budget->ttbp; + u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + u32 count; + + /* Ensure streamed PCI data is synced to CPU */ + dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, + budget->pt.nents, DMA_FROM_DEVICE); + + /* nearest lower position divisible by 188 */ + newdma -= newdma % 188; + + if (newdma >= budget->buffer_size) + return; + + budget->ttbp = newdma; + + if (budget->feeding == 0 || newdma == olddma) + return; + + if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ + count = newdma - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + } else { /* wraparound, dump olddma..buflen and 0..newdma */ + count = budget->buffer_size - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + count += newdma; + dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); + } + + if (count > budget->buffer_warning_threshold) + budget->buffer_warnings++; + + if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { + printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", + budget->dev->name, __func__, budget->buffer_warnings, count); + budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; + budget->buffer_warnings = 0; + } +} + + +static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config, + int addr, int count, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, MC2, (2 << 16) | 2); + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + result = saa7146_read(saa, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + return result; +} + +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + return ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); +} + +static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config, + int addr, int count, u32 value, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, DEBI_AD, value); + saa7146_write(saa, MC2, (2 << 16) | 2); + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + return result < 0 ? result : 0; +} + +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + return ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); +} + + +/**************************************************************************** + * DVB API SECTION + ****************************************************************************/ + +static int budget_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + if (!demux->dmx.frontend) + return -EINVAL; + + spin_lock(&budget->feedlock); + feed->pusi_seen = false; /* have a clean section start */ + if (budget->feeding++ == 0) + status = start_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + spin_lock(&budget->feedlock); + if (--budget->feeding == 0) + status = stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_register(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + int ret; + + dprintk(2, "budget: %p\n", budget); + + dvbdemux->priv = (void *) budget; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = budget_start_feed; + dvbdemux->stop_feed = budget_stop_feed; + dvbdemux->write_to_decoder = NULL; + + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&budget->demux); + + budget->dmxdev.filternum = 256; + budget->dmxdev.demux = &dvbdemux->dmx; + budget->dmxdev.capabilities = 0; + + dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); + + budget->hw_frontend.source = DMX_FRONTEND_0; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); + + if (ret < 0) + goto err_release_dmx; + + budget->mem_frontend.source = DMX_MEMORY_FE; + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); + if (ret < 0) + goto err_release_dmx; + + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); + if (ret < 0) + goto err_release_dmx; + + dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); + + return 0; + +err_release_dmx: + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); + return ret; +} + +static void budget_unregister(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + + dprintk(2, "budget: %p\n", budget); + + dvb_net_release(&budget->dvb_net); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); + + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); +} + +int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums) +{ + int ret = 0; + struct budget_info *bi = info->ext_priv; + int max_bufsize; + int height_mask; + + memset(budget, 0, sizeof(struct budget)); + + dprintk(2, "dev: %p, budget: %p\n", dev, budget); + + budget->card = bi; + budget->dev = (struct saa7146_dev *) dev; + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + budget->buffer_width = TS_WIDTH_ACTIVY; + max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; + height_mask = TS_HEIGHT_MASK_ACTIVY; + break; + + case BUDGET_KNC1C: + case BUDGET_KNC1CP: + case BUDGET_CIN1200C: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + case BUDGET_CIN1200C_MK3: + budget->buffer_width = TS_WIDTH_DVBC; + max_bufsize = TS_MAX_BUFSIZE_K_DVBC; + height_mask = TS_HEIGHT_MASK_DVBC; + break; + + default: + budget->buffer_width = TS_WIDTH; + max_bufsize = TS_MAX_BUFSIZE_K; + height_mask = TS_HEIGHT_MASK; + } + + if (dma_buffer_size < TS_MIN_BUFSIZE_K) + dma_buffer_size = TS_MIN_BUFSIZE_K; + else if (dma_buffer_size > max_bufsize) + dma_buffer_size = max_bufsize; + + budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; + if (budget->buffer_height > 0xfff) { + budget->buffer_height /= 2; + budget->buffer_height &= height_mask; + budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; + } else { + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + } + budget->buffer_warning_threshold = budget->buffer_size * 80/100; + budget->buffer_warnings = 0; + budget->buffer_warning_time = jiffies; + + dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", + budget->dev->name, + budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", + budget->buffer_width, budget->buffer_height); + printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); + + ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, + owner, &budget->dev->pci->dev, adapter_nums); + if (ret < 0) + return ret; + + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, DD1_INIT, 0x02000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + if (bi->type != BUDGET_FS_ACTIVY) + budget->video_port = BUDGET_VIDEO_PORTB; + else + budget->video_port = BUDGET_VIDEO_PORTA; + spin_lock_init(&budget->feedlock); + spin_lock_init(&budget->debilock); + + /* the Siemens DVB needs this if you want to have the i2c chips + get recognized before the main driver is loaded */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ + + strscpy(budget->i2c_adap.name, budget->card->name, + sizeof(budget->i2c_adap.name)); + + saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); + strscpy(budget->i2c_adap.name, budget->card->name, + sizeof(budget->i2c_adap.name)); + + if (i2c_add_adapter(&budget->i2c_adap) < 0) { + ret = -ENOMEM; + goto err_dvb_unregister; + } + + ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); + + budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); + if (NULL == budget->grabbing) { + ret = -ENOMEM; + goto err_del_i2c; + } + + saa7146_write(dev, PCI_BT_V1, 0x001c0000); + /* upload all */ + saa7146_write(dev, GPIO_CTRL, 0x000000); + + tasklet_setup(&budget->vpe_tasklet, vpeirq); + + /* frontend power on */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + if ((ret = budget_register(budget)) == 0) + return 0; /* Everything OK */ + + /* An error occurred, cleanup resources */ + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + +err_del_i2c: + i2c_del_adapter(&budget->i2c_adap); + +err_dvb_unregister: + dvb_unregister_adapter(&budget->dvb_adapter); + + return ret; +} + +void ttpci_budget_init_hooks(struct budget *budget) +{ + if (budget->dvb_frontend && !budget->read_fe_status) { + budget->read_fe_status = budget->dvb_frontend->ops.read_status; + budget->dvb_frontend->ops.read_status = budget_read_fe_status; + } +} + +int ttpci_budget_deinit(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + budget_unregister(budget); + + tasklet_kill(&budget->vpe_tasklet); + + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + + i2c_del_adapter(&budget->i2c_adap); + + dvb_unregister_adapter(&budget->dvb_adapter); + + return 0; +} + +void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + dprintk(8, "dev: %p, budget: %p\n", dev, budget); + + if (*isr & MASK_10) + tasklet_schedule(&budget->vpe_tasklet); +} + +void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + spin_lock(&budget->feedlock); + budget->video_port = video_port; + if (budget->feeding) { + stop_ts_capture(budget); + start_ts_capture(budget); + } + spin_unlock(&budget->feedlock); +} + +EXPORT_SYMBOL_GPL(ttpci_budget_debiread); +EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); +EXPORT_SYMBOL_GPL(ttpci_budget_init); +EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); +EXPORT_SYMBOL_GPL(ttpci_budget_deinit); +EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); +EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +EXPORT_SYMBOL_GPL(budget_debug); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget.c new file mode 100644 index 000000000000..a88711a3ac7f --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget.c @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold + * + * Copyright (C) 2002 Ralph Metzler + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher , + * Oliver Endriss and + * Andreas 'randy' Weinberger + * + * the project's page is at https://linuxtv.org + */ + +#include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "ves1820.h" +#include "l64781.h" +#include "tda8083.h" +#include "s5h1420.h" +#include "tda10086.h" +#include "tda826x.h" +#include "lnbp21.h" +#include "bsru6.h" +#include "bsbe1.h" +#include "tdhd1.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "isl6423.h" +#include "lnbh24.h" + + +static int diseqc_method; +module_param(diseqc_method, int, 0444); +MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void Set22K (struct budget *budget, int state) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +} + +/* Diseqc functions only for TT Budget card */ +/* taken from the Skyvision DVB driver by + Ralph Metzler */ + +static void DiseqcSendBit (struct budget *budget, int data) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + udelay(data ? 500 : 1000); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + udelay(data ? 1000 : 500); +} + +static void DiseqcSendByte (struct budget *budget, int data) +{ + int i, par=1, d; + + dprintk(2, "budget: %p\n", budget); + + for (i=7; i>=0; i--) { + d = (data>>i)&1; + par ^= d; + DiseqcSendBit(budget, d); + } + + DiseqcSendBit(budget, par); +} + +static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +{ + struct saa7146_dev *dev=budget->dev; + int i; + + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + mdelay(16); + + for (i=0; idev; + + dprintk(2, "budget: %p\n", budget); + + switch (voltage) { + case SEC_VOLTAGE_13: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); + break; + case SEC_VOLTAGE_18: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + break; + case SEC_VOLTAGE_OFF: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int siemens_budget_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + return SetVoltage_Activy (budget, voltage); +} + +static int budget_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + switch (tone) { + case SEC_TONE_ON: + Set22K (budget, 1); + break; + + case SEC_TONE_OFF: + Set22K (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, 0, NULL, minicmd); + + return 0; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (c->frequency + 479500) / 125; + + if (c->frequency > 2000000) + pwr = 3; + else if (c->frequency > 1800000) + pwr = 2; + else if (c->frequency > 1600000) + pwr = 1; + else if (c->frequency > 1200000) + pwr = 0; + else if (c->frequency >= 1100000) + pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = +{ + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (c->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + +static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = fe->dvb->priv; + u8 *tuner_addr = fe->tuner_priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; + + if (tuner_addr) + msg.addr = *tuner_addr; + else + msg.addr = 0x61; + + div = (36125000 + c->frequency) / 166666; + + cfg = 0x88; + + if (c->frequency < 175000000) + cpump = 2; + else if (c->frequency < 390000000) + cpump = 1; + else if (c->frequency < 470000000) + cpump = 2; + else if (c->frequency < 750000000) + cpump = 1; + else + cpump = 3; + + if (c->frequency < 175000000) + band_select = 0x0e; + else if (c->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, +}; + +static struct l64781_config grundig_29504_401_config_activy = { + .demod_address = 0x54, +}; + +static u8 tuner_address_grundig_29504_401_activy = 0x60; + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + +static int s5h1420_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 1000; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xc2; + + if (div < 1450) + data[3] = 0x00; + else if (div < 1850) + data[3] = 0x40; + else if (div < 2000) + data[3] = 0x80; + else + data[3] = 0xc0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + + return 0; +} + +static struct s5h1420_config s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .cdclk_polarity = 1, +}; + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static const struct stv0299_config alps_bsru6_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, +}; + +static const struct stv0299_config alps_bsbe1_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsbe1_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsbe1_set_symbol_rate, +}; + +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *)fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + + +static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) +{ + u8 val; + struct i2c_msg msg[] = { + { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; +} + +static u8 read_pwm(struct budget* budget) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static struct stv090x_config tt1600_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 13500000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_DVBCI, + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + + .repeater_level = STV090x_RPTLEVEL_16, + + .tuner_init = NULL, + .tuner_sleep = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config tt1600_stv6110x_config = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 2, +}; + +static struct isl6423_config tt1600_isl6423_config = { + .current_max = SEC_CURRENT_515m, + .curlim = SEC_CURRENT_LIM_ON, + .mod_extern = 1, + .addr = 0x08, +}; + +static void frontend_init(struct budget *budget) +{ + (void)alps_bsbe1_config; /* avoid warning */ + + switch(budget->dev->pci->subsystem_device) { + case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) + case 0x1013: + // try the ALPS BSRV2 first of all + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + } + break; + } + break; + + case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) + + budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } + break; + + case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) + + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + budget->dvb_frontend->tuner_priv = NULL; + break; + } + break; + + case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + break; + } + break; + + case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ + { + int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); + + if (subtype < 0) + break; + /* fixme: find a better way to identify the card */ + if (subtype < 0x36) { + /* assume ALPS BSRU6 */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } else { + /* assume ALPS BSBE1 */ + /* reset tuner */ + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); + msleep(250); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } + break; + } + + case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + } + break; + + case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ + budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + } + break; + + case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + } + break; + + case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) + { + struct dvb_frontend *fe; + + fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params; + budget->dvb_frontend = fe; + if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap, + 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + } + fallthrough; + case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) + { + struct dvb_frontend *fe; + + // gpio2 is connected to CLB - reset it + leave it high + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(1); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(1); + + fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); + if (fe) { + budget->dvb_frontend = fe; + if (dvb_attach(tda826x_attach, fe, 0x60, + &budget->i2c_adap, 0) == NULL) + printk("%s: No tda826x found!\n", __func__); + if (dvb_attach(lnbp21_attach, fe, + &budget->i2c_adap, 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + } + fallthrough; + + case 0x101c: { /* TT S2-1600 */ + const struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(isl6423_attach, + budget->dvb_frontend, + &budget->i2c_adap, + &tt1600_isl6423_config) == NULL) { + printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + + case 0x1020: { /* Omicom S2 */ + const struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: Omicom S2 detected\n"); + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(lnbh24_attach, + budget->dvb_frontend, + &budget->i2c_adap, + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x14>>1) == NULL) { + printk(KERN_ERR + "No LNBH24 found!\n"); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) + goto error_out; + } + return; + +error_out: + printk("budget: Frontend registration failed!\n"); + dvb_frontend_detach(budget->dvb_frontend); + budget->dvb_frontend = NULL; + return; +} + +static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +{ + struct budget *budget = NULL; + int err; + + budget = kmalloc(sizeof(struct budget), GFP_KERNEL); + if( NULL == budget ) { + return -ENOMEM; + } + + dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); + + dev->ext_priv = budget; + + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + printk("==> failed\n"); + kfree (budget); + return err; + } + + budget->dvb_adapter.priv = budget; + frontend_init(budget); + + ttpci_budget_init_hooks(budget); + + return 0; +} + +static int budget_detach (struct saa7146_dev* dev) +{ + struct budget *budget = (struct budget*) dev->ext_priv; + int err; + + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } + + err = ttpci_budget_deinit (budget); + + kfree (budget); + dev->ext_priv = NULL; + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), + MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), + MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), + MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), + MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), + MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), + MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), + MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), + MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), + MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), + MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), + MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = pci_tbl, + .attach = budget_attach, + .detach = budget_detach, + + .irq_mask = MASK_10, + .irq_func = ttpci_budget_irq10_handler, +}; + +static int __init budget_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_init); +module_exit(budget_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget.h b/drivers/staging/media/deprecated/saa7146/ttpci/budget.h new file mode 100644 index 000000000000..82cc0df492b3 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __BUDGET_DVB__ +#define __BUDGET_DVB__ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../common/saa7146.h" + +extern int budget_debug; + +#ifdef dprintk +#undef dprintk +#endif + +#define dprintk(level, fmt, arg...) do { \ + if (level & budget_debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ + __func__, ##arg); \ +} while (0) + +#define TS_SIZE 188 + +struct budget_info { + char *name; + int type; +}; + +/* place to store all the necessary device information */ +struct budget { + + /* devices */ + struct dvb_device dvb_dev; + struct dvb_net dvb_net; + + struct saa7146_dev *dev; + + struct i2c_adapter i2c_adap; + struct budget_info *card; + + unsigned char *grabbing; + struct saa7146_pgtable pt; + + struct tasklet_struct fidb_tasklet; + struct tasklet_struct vpe_tasklet; + + struct dmxdev dmxdev; + struct dvb_demux demux; + + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + + int ci_present; + int video_port; + + u32 buffer_width; + u32 buffer_height; + u32 buffer_size; + u32 buffer_warning_threshold; + u32 buffer_warnings; + unsigned long buffer_warning_time; + + u32 ttbp; + int feeding; + + spinlock_t feedlock; + + spinlock_t debilock; + + struct dvb_adapter dvb_adapter; + struct dvb_frontend *dvb_frontend; + int (*read_fe_status)(struct dvb_frontend *fe, enum fe_status *status); + int fe_synced; + + void *priv; +}; + +#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ +static struct budget_info x_var ## _info = { \ + .name=x_name, \ + .type=x_type }; \ +static struct saa7146_pci_extension_data x_var = { \ + .ext_priv = &x_var ## _info, \ + .ext = &budget_extension }; + +#define BUDGET_TT 0 +#define BUDGET_TT_HW_DISEQC 1 +#define BUDGET_PATCH 3 +#define BUDGET_FS_ACTIVY 4 +#define BUDGET_CIN1200S 5 +#define BUDGET_CIN1200C 6 +#define BUDGET_CIN1200T 7 +#define BUDGET_KNC1S 8 +#define BUDGET_KNC1C 9 +#define BUDGET_KNC1T 10 +#define BUDGET_KNC1SP 11 +#define BUDGET_KNC1CP 12 +#define BUDGET_KNC1TP 13 +#define BUDGET_TVSTAR 14 +#define BUDGET_CIN1200C_MK3 15 +#define BUDGET_KNC1C_MK3 16 +#define BUDGET_KNC1CP_MK3 17 +#define BUDGET_KNC1S2 18 +#define BUDGET_KNC1C_TDA10024 19 + +#define BUDGET_VIDEO_PORTA 0 +#define BUDGET_VIDEO_PORTB 1 + +extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums); +extern void ttpci_budget_init_hooks(struct budget *budget); +extern int ttpci_budget_deinit(struct budget *budget); +extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); +extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); +extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop); +extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, + int uselocks, int nobusyloop); + +#endif diff --git a/include/media/drv-intf/saa7146.h b/include/media/drv-intf/saa7146.h deleted file mode 100644 index 71ce63c99cb4..000000000000 --- a/include/media/drv-intf/saa7146.h +++ /dev/null @@ -1,472 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __SAA7146__ -#define __SAA7146__ - -#include /* for delay-stuff */ -#include /* for kmalloc/kfree */ -#include /* for pci-config-stuff, vendor ids etc. */ -#include /* for "__init" */ -#include /* for IMMEDIATE_BH */ -#include /* for kernel module loader */ -#include /* for i2c subsystem */ -#include /* for accessing devices */ -#include -#include -#include -#include -#include - -#include /* for vmalloc() */ -#include /* for vmalloc_to_page() */ - -#define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr))) -#define saa7146_read(sxy,adr) readl(sxy->mem+(adr)) - -extern unsigned int saa7146_debug; - -#ifndef DEBUG_VARIABLE - #define DEBUG_VARIABLE saa7146_debug -#endif - -#define ERR(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) - -#define _DBG(mask, fmt, ...) \ -do { \ - if (DEBUG_VARIABLE & mask) \ - pr_debug("%s(): " fmt, __func__, ##__VA_ARGS__); \ -} while (0) - -/* simple debug messages */ -#define DEB_S(fmt, ...) _DBG(0x01, fmt, ##__VA_ARGS__) -/* more detailed debug messages */ -#define DEB_D(fmt, ...) _DBG(0x02, fmt, ##__VA_ARGS__) -/* print enter and exit of functions */ -#define DEB_EE(fmt, ...) _DBG(0x04, fmt, ##__VA_ARGS__) -/* i2c debug messages */ -#define DEB_I2C(fmt, ...) _DBG(0x08, fmt, ##__VA_ARGS__) -/* vbi debug messages */ -#define DEB_VBI(fmt, ...) _DBG(0x10, fmt, ##__VA_ARGS__) -/* interrupt debug messages */ -#define DEB_INT(fmt, ...) _DBG(0x20, fmt, ##__VA_ARGS__) -/* capture debug messages */ -#define DEB_CAP(fmt, ...) _DBG(0x40, fmt, ##__VA_ARGS__) - -#define SAA7146_ISR_CLEAR(x,y) \ - saa7146_write(x, ISR, (y)); - -struct module; - -struct saa7146_dev; -struct saa7146_extension; -struct saa7146_vv; - -/* saa7146 page table */ -struct saa7146_pgtable { - unsigned int size; - __le32 *cpu; - dma_addr_t dma; - /* used for offsets for u,v planes for planar capture modes */ - unsigned long offset; - /* used for custom pagetables (used for example by budget dvb cards) */ - struct scatterlist *slist; - int nents; -}; - -struct saa7146_pci_extension_data { - struct saa7146_extension *ext; - void *ext_priv; /* most likely a name string */ -}; - -#define MAKE_EXTENSION_PCI(x_var, x_vendor, x_device) \ - { \ - .vendor = PCI_VENDOR_ID_PHILIPS, \ - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, \ - .subvendor = x_vendor, \ - .subdevice = x_device, \ - .driver_data = (unsigned long)& x_var, \ - } - -struct saa7146_extension -{ - char name[32]; /* name of the device */ -#define SAA7146_USE_I2C_IRQ 0x1 -#define SAA7146_I2C_SHORT_DELAY 0x2 - int flags; - - /* pairs of subvendor and subdevice ids for - supported devices, last entry 0xffff, 0xfff */ - struct module *module; - struct pci_driver driver; - const struct pci_device_id *pci_tbl; - - /* extension functions */ - int (*probe)(struct saa7146_dev *); - int (*attach)(struct saa7146_dev *, struct saa7146_pci_extension_data *); - int (*detach)(struct saa7146_dev*); - - u32 irq_mask; /* mask to indicate, which irq-events are handled by the extension */ - void (*irq_func)(struct saa7146_dev*, u32* irq_mask); -}; - -struct saa7146_dma -{ - dma_addr_t dma_handle; - __le32 *cpu_addr; -}; - -struct saa7146_dev -{ - struct module *module; - - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - - /* different device locks */ - spinlock_t slock; - struct mutex v4l2_lock; - - unsigned char __iomem *mem; /* pointer to mapped IO memory */ - u32 revision; /* chip revision; needed for bug-workarounds*/ - - /* pci-device & irq stuff*/ - char name[32]; - struct pci_dev *pci; - u32 int_todo; - spinlock_t int_slock; - - /* extension handling */ - struct saa7146_extension *ext; /* indicates if handled by extension */ - void *ext_priv; /* pointer for extension private use (most likely some private data) */ - struct saa7146_ext_vv *ext_vv_data; - - /* per device video/vbi information (if available) */ - struct saa7146_vv *vv_data; - void (*vv_callback)(struct saa7146_dev *dev, unsigned long status); - - /* i2c-stuff */ - struct mutex i2c_lock; - - u32 i2c_bitrate; - struct saa7146_dma d_i2c; /* pointer to i2c memory */ - wait_queue_head_t i2c_wq; - int i2c_op; - - /* memories */ - struct saa7146_dma d_rps0; - struct saa7146_dma d_rps1; -}; - -static inline struct saa7146_dev *to_saa7146_dev(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct saa7146_dev, v4l2_dev); -} - -/* from saa7146_i2c.c */ -int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate); - -/* from saa7146_core.c */ -int saa7146_register_extension(struct saa7146_extension*); -int saa7146_unregister_extension(struct saa7146_extension*); -struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc); -int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt); -void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt); -int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length ); -void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt); -void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt); -void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data); -int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop); - -/* some memory sizes */ -#define SAA7146_I2C_MEM ( 1*PAGE_SIZE) -#define SAA7146_RPS_MEM ( 1*PAGE_SIZE) - -/* some i2c constants */ -#define SAA7146_I2C_TIMEOUT 100 /* i2c-timeout-value in ms */ -#define SAA7146_I2C_RETRIES 3 /* how many times shall we retry an i2c-operation? */ -#define SAA7146_I2C_DELAY 5 /* time we wait after certain i2c-operations */ - -/* unsorted defines */ -#define ME1 0x0000000800 -#define PV1 0x0000000008 - -/* gpio defines */ -#define SAA7146_GPIO_INPUT 0x00 -#define SAA7146_GPIO_IRQHI 0x10 -#define SAA7146_GPIO_IRQLO 0x20 -#define SAA7146_GPIO_IRQHL 0x30 -#define SAA7146_GPIO_OUTLO 0x40 -#define SAA7146_GPIO_OUTHI 0x50 - -/* debi defines */ -#define DEBINOSWAP 0x000e0000 - -/* define for the register programming sequencer (rps) */ -#define CMD_NOP 0x00000000 /* No operation */ -#define CMD_CLR_EVENT 0x00000000 /* Clear event */ -#define CMD_SET_EVENT 0x10000000 /* Set signal event */ -#define CMD_PAUSE 0x20000000 /* Pause */ -#define CMD_CHECK_LATE 0x30000000 /* Check late */ -#define CMD_UPLOAD 0x40000000 /* Upload */ -#define CMD_STOP 0x50000000 /* Stop */ -#define CMD_INTERRUPT 0x60000000 /* Interrupt */ -#define CMD_JUMP 0x80000000 /* Jump */ -#define CMD_WR_REG 0x90000000 /* Write (load) register */ -#define CMD_RD_REG 0xa0000000 /* Read (store) register */ -#define CMD_WR_REG_MASK 0xc0000000 /* Write register with mask */ - -#define CMD_OAN MASK_27 -#define CMD_INV MASK_26 -#define CMD_SIG4 MASK_25 -#define CMD_SIG3 MASK_24 -#define CMD_SIG2 MASK_23 -#define CMD_SIG1 MASK_22 -#define CMD_SIG0 MASK_21 -#define CMD_O_FID_B MASK_14 -#define CMD_E_FID_B MASK_13 -#define CMD_O_FID_A MASK_12 -#define CMD_E_FID_A MASK_11 - -/* some events and command modifiers for rps1 squarewave generator */ -#define EVT_HS (1<<15) // Source Line Threshold reached -#define EVT_VBI_B (1<<9) // VSYNC Event -#define RPS_OAN (1<<27) // 1: OR events, 0: AND events -#define RPS_INV (1<<26) // Invert (compound) event -#define GPIO3_MSK 0xFF000000 // GPIO #3 control bits - -/* Bit mask constants */ -#define MASK_00 0x00000001 /* Mask value for bit 0 */ -#define MASK_01 0x00000002 /* Mask value for bit 1 */ -#define MASK_02 0x00000004 /* Mask value for bit 2 */ -#define MASK_03 0x00000008 /* Mask value for bit 3 */ -#define MASK_04 0x00000010 /* Mask value for bit 4 */ -#define MASK_05 0x00000020 /* Mask value for bit 5 */ -#define MASK_06 0x00000040 /* Mask value for bit 6 */ -#define MASK_07 0x00000080 /* Mask value for bit 7 */ -#define MASK_08 0x00000100 /* Mask value for bit 8 */ -#define MASK_09 0x00000200 /* Mask value for bit 9 */ -#define MASK_10 0x00000400 /* Mask value for bit 10 */ -#define MASK_11 0x00000800 /* Mask value for bit 11 */ -#define MASK_12 0x00001000 /* Mask value for bit 12 */ -#define MASK_13 0x00002000 /* Mask value for bit 13 */ -#define MASK_14 0x00004000 /* Mask value for bit 14 */ -#define MASK_15 0x00008000 /* Mask value for bit 15 */ -#define MASK_16 0x00010000 /* Mask value for bit 16 */ -#define MASK_17 0x00020000 /* Mask value for bit 17 */ -#define MASK_18 0x00040000 /* Mask value for bit 18 */ -#define MASK_19 0x00080000 /* Mask value for bit 19 */ -#define MASK_20 0x00100000 /* Mask value for bit 20 */ -#define MASK_21 0x00200000 /* Mask value for bit 21 */ -#define MASK_22 0x00400000 /* Mask value for bit 22 */ -#define MASK_23 0x00800000 /* Mask value for bit 23 */ -#define MASK_24 0x01000000 /* Mask value for bit 24 */ -#define MASK_25 0x02000000 /* Mask value for bit 25 */ -#define MASK_26 0x04000000 /* Mask value for bit 26 */ -#define MASK_27 0x08000000 /* Mask value for bit 27 */ -#define MASK_28 0x10000000 /* Mask value for bit 28 */ -#define MASK_29 0x20000000 /* Mask value for bit 29 */ -#define MASK_30 0x40000000 /* Mask value for bit 30 */ -#define MASK_31 0x80000000 /* Mask value for bit 31 */ - -#define MASK_B0 0x000000ff /* Mask value for byte 0 */ -#define MASK_B1 0x0000ff00 /* Mask value for byte 1 */ -#define MASK_B2 0x00ff0000 /* Mask value for byte 2 */ -#define MASK_B3 0xff000000 /* Mask value for byte 3 */ - -#define MASK_W0 0x0000ffff /* Mask value for word 0 */ -#define MASK_W1 0xffff0000 /* Mask value for word 1 */ - -#define MASK_PA 0xfffffffc /* Mask value for physical address */ -#define MASK_PR 0xfffffffe /* Mask value for protection register */ -#define MASK_ER 0xffffffff /* Mask value for the entire register */ - -#define MASK_NONE 0x00000000 /* No mask */ - -/* register aliases */ -#define BASE_ODD1 0x00 /* Video DMA 1 registers */ -#define BASE_EVEN1 0x04 -#define PROT_ADDR1 0x08 -#define PITCH1 0x0C -#define BASE_PAGE1 0x10 /* Video DMA 1 base page */ -#define NUM_LINE_BYTE1 0x14 - -#define BASE_ODD2 0x18 /* Video DMA 2 registers */ -#define BASE_EVEN2 0x1C -#define PROT_ADDR2 0x20 -#define PITCH2 0x24 -#define BASE_PAGE2 0x28 /* Video DMA 2 base page */ -#define NUM_LINE_BYTE2 0x2C - -#define BASE_ODD3 0x30 /* Video DMA 3 registers */ -#define BASE_EVEN3 0x34 -#define PROT_ADDR3 0x38 -#define PITCH3 0x3C -#define BASE_PAGE3 0x40 /* Video DMA 3 base page */ -#define NUM_LINE_BYTE3 0x44 - -#define PCI_BT_V1 0x48 /* Video/FIFO 1 */ -#define PCI_BT_V2 0x49 /* Video/FIFO 2 */ -#define PCI_BT_V3 0x4A /* Video/FIFO 3 */ -#define PCI_BT_DEBI 0x4B /* DEBI */ -#define PCI_BT_A 0x4C /* Audio */ - -#define DD1_INIT 0x50 /* Init setting of DD1 interface */ - -#define DD1_STREAM_B 0x54 /* DD1 B video data stream handling */ -#define DD1_STREAM_A 0x56 /* DD1 A video data stream handling */ - -#define BRS_CTRL 0x58 /* BRS control register */ -#define HPS_CTRL 0x5C /* HPS control register */ -#define HPS_V_SCALE 0x60 /* HPS vertical scale */ -#define HPS_V_GAIN 0x64 /* HPS vertical ACL and gain */ -#define HPS_H_PRESCALE 0x68 /* HPS horizontal prescale */ -#define HPS_H_SCALE 0x6C /* HPS horizontal scale */ -#define BCS_CTRL 0x70 /* BCS control */ -#define CHROMA_KEY_RANGE 0x74 -#define CLIP_FORMAT_CTRL 0x78 /* HPS outputs formats & clipping */ - -#define DEBI_CONFIG 0x7C -#define DEBI_COMMAND 0x80 -#define DEBI_PAGE 0x84 -#define DEBI_AD 0x88 - -#define I2C_TRANSFER 0x8C -#define I2C_STATUS 0x90 - -#define BASE_A1_IN 0x94 /* Audio 1 input DMA */ -#define PROT_A1_IN 0x98 -#define PAGE_A1_IN 0x9C - -#define BASE_A1_OUT 0xA0 /* Audio 1 output DMA */ -#define PROT_A1_OUT 0xA4 -#define PAGE_A1_OUT 0xA8 - -#define BASE_A2_IN 0xAC /* Audio 2 input DMA */ -#define PROT_A2_IN 0xB0 -#define PAGE_A2_IN 0xB4 - -#define BASE_A2_OUT 0xB8 /* Audio 2 output DMA */ -#define PROT_A2_OUT 0xBC -#define PAGE_A2_OUT 0xC0 - -#define RPS_PAGE0 0xC4 /* RPS task 0 page register */ -#define RPS_PAGE1 0xC8 /* RPS task 1 page register */ - -#define RPS_THRESH0 0xCC /* HBI threshold for task 0 */ -#define RPS_THRESH1 0xD0 /* HBI threshold for task 1 */ - -#define RPS_TOV0 0xD4 /* RPS timeout for task 0 */ -#define RPS_TOV1 0xD8 /* RPS timeout for task 1 */ - -#define IER 0xDC /* Interrupt enable register */ - -#define GPIO_CTRL 0xE0 /* GPIO 0-3 register */ - -#define EC1SSR 0xE4 /* Event cnt set 1 source select */ -#define EC2SSR 0xE8 /* Event cnt set 2 source select */ -#define ECT1R 0xEC /* Event cnt set 1 thresholds */ -#define ECT2R 0xF0 /* Event cnt set 2 thresholds */ - -#define ACON1 0xF4 -#define ACON2 0xF8 - -#define MC1 0xFC /* Main control register 1 */ -#define MC2 0x100 /* Main control register 2 */ - -#define RPS_ADDR0 0x104 /* RPS task 0 address register */ -#define RPS_ADDR1 0x108 /* RPS task 1 address register */ - -#define ISR 0x10C /* Interrupt status register */ -#define PSR 0x110 /* Primary status register */ -#define SSR 0x114 /* Secondary status register */ - -#define EC1R 0x118 /* Event counter set 1 register */ -#define EC2R 0x11C /* Event counter set 2 register */ - -#define PCI_VDP1 0x120 /* Video DMA pointer of FIFO 1 */ -#define PCI_VDP2 0x124 /* Video DMA pointer of FIFO 2 */ -#define PCI_VDP3 0x128 /* Video DMA pointer of FIFO 3 */ -#define PCI_ADP1 0x12C /* Audio DMA pointer of audio out 1 */ -#define PCI_ADP2 0x130 /* Audio DMA pointer of audio in 1 */ -#define PCI_ADP3 0x134 /* Audio DMA pointer of audio out 2 */ -#define PCI_ADP4 0x138 /* Audio DMA pointer of audio in 2 */ -#define PCI_DMA_DDP 0x13C /* DEBI DMA pointer */ - -#define LEVEL_REP 0x140, -#define A_TIME_SLOT1 0x180, /* from 180 - 1BC */ -#define A_TIME_SLOT2 0x1C0, /* from 1C0 - 1FC */ - -/* isr masks */ -#define SPCI_PPEF 0x80000000 /* PCI parity error */ -#define SPCI_PABO 0x40000000 /* PCI access error (target or master abort) */ -#define SPCI_PPED 0x20000000 /* PCI parity error on 'real time data' */ -#define SPCI_RPS_I1 0x10000000 /* Interrupt issued by RPS1 */ -#define SPCI_RPS_I0 0x08000000 /* Interrupt issued by RPS0 */ -#define SPCI_RPS_LATE1 0x04000000 /* RPS task 1 is late */ -#define SPCI_RPS_LATE0 0x02000000 /* RPS task 0 is late */ -#define SPCI_RPS_E1 0x01000000 /* RPS error from task 1 */ -#define SPCI_RPS_E0 0x00800000 /* RPS error from task 0 */ -#define SPCI_RPS_TO1 0x00400000 /* RPS timeout task 1 */ -#define SPCI_RPS_TO0 0x00200000 /* RPS timeout task 0 */ -#define SPCI_UPLD 0x00100000 /* RPS in upload */ -#define SPCI_DEBI_S 0x00080000 /* DEBI status */ -#define SPCI_DEBI_E 0x00040000 /* DEBI error */ -#define SPCI_IIC_S 0x00020000 /* I2C status */ -#define SPCI_IIC_E 0x00010000 /* I2C error */ -#define SPCI_A2_IN 0x00008000 /* Audio 2 input DMA protection / limit */ -#define SPCI_A2_OUT 0x00004000 /* Audio 2 output DMA protection / limit */ -#define SPCI_A1_IN 0x00002000 /* Audio 1 input DMA protection / limit */ -#define SPCI_A1_OUT 0x00001000 /* Audio 1 output DMA protection / limit */ -#define SPCI_AFOU 0x00000800 /* Audio FIFO over- / underflow */ -#define SPCI_V_PE 0x00000400 /* Video protection address */ -#define SPCI_VFOU 0x00000200 /* Video FIFO over- / underflow */ -#define SPCI_FIDA 0x00000100 /* Field ID video port A */ -#define SPCI_FIDB 0x00000080 /* Field ID video port B */ -#define SPCI_PIN3 0x00000040 /* GPIO pin 3 */ -#define SPCI_PIN2 0x00000020 /* GPIO pin 2 */ -#define SPCI_PIN1 0x00000010 /* GPIO pin 1 */ -#define SPCI_PIN0 0x00000008 /* GPIO pin 0 */ -#define SPCI_ECS 0x00000004 /* Event counter 1, 2, 4, 5 */ -#define SPCI_EC3S 0x00000002 /* Event counter 3 */ -#define SPCI_EC0S 0x00000001 /* Event counter 0 */ - -/* i2c */ -#define SAA7146_I2C_ABORT (1<<7) -#define SAA7146_I2C_SPERR (1<<6) -#define SAA7146_I2C_APERR (1<<5) -#define SAA7146_I2C_DTERR (1<<4) -#define SAA7146_I2C_DRERR (1<<3) -#define SAA7146_I2C_AL (1<<2) -#define SAA7146_I2C_ERR (1<<1) -#define SAA7146_I2C_BUSY (1<<0) - -#define SAA7146_I2C_START (0x3) -#define SAA7146_I2C_CONT (0x2) -#define SAA7146_I2C_STOP (0x1) -#define SAA7146_I2C_NOP (0x0) - -#define SAA7146_I2C_BUS_BIT_RATE_6400 (0x500) -#define SAA7146_I2C_BUS_BIT_RATE_3200 (0x100) -#define SAA7146_I2C_BUS_BIT_RATE_480 (0x400) -#define SAA7146_I2C_BUS_BIT_RATE_320 (0x600) -#define SAA7146_I2C_BUS_BIT_RATE_240 (0x700) -#define SAA7146_I2C_BUS_BIT_RATE_120 (0x000) -#define SAA7146_I2C_BUS_BIT_RATE_80 (0x200) -#define SAA7146_I2C_BUS_BIT_RATE_60 (0x300) - -static inline void SAA7146_IER_DISABLE(struct saa7146_dev *x, unsigned y) -{ - unsigned long flags; - spin_lock_irqsave(&x->int_slock, flags); - saa7146_write(x, IER, saa7146_read(x, IER) & ~y); - spin_unlock_irqrestore(&x->int_slock, flags); -} - -static inline void SAA7146_IER_ENABLE(struct saa7146_dev *x, unsigned y) -{ - unsigned long flags; - spin_lock_irqsave(&x->int_slock, flags); - saa7146_write(x, IER, saa7146_read(x, IER) | y); - spin_unlock_irqrestore(&x->int_slock, flags); -} - -#endif diff --git a/include/media/drv-intf/saa7146_vv.h b/include/media/drv-intf/saa7146_vv.h deleted file mode 100644 index 635805fb35e8..000000000000 --- a/include/media/drv-intf/saa7146_vv.h +++ /dev/null @@ -1,266 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __SAA7146_VV__ -#define __SAA7146_VV__ - -#include -#include -#include -#include -#include - -#define MAX_SAA7146_CAPTURE_BUFFERS 32 /* arbitrary */ -#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ - -#define WRITE_RPS0(x) do { \ - dev->d_rps0.cpu_addr[ count++ ] = cpu_to_le32(x); \ - } while (0); - -#define WRITE_RPS1(x) do { \ - dev->d_rps1.cpu_addr[ count++ ] = cpu_to_le32(x); \ - } while (0); - -struct saa7146_video_dma { - u32 base_odd; - u32 base_even; - u32 prot_addr; - u32 pitch; - u32 base_page; - u32 num_line_byte; -}; - -#define FORMAT_BYTE_SWAP 0x1 -#define FORMAT_IS_PLANAR 0x2 - -struct saa7146_format { - u32 pixelformat; - u32 trans; - u8 depth; - u8 flags; - u8 swap; -}; - -struct saa7146_standard -{ - char *name; - v4l2_std_id id; - - int v_offset; /* number of lines of vertical offset before processing */ - int v_field; /* number of lines in a field for HPS to process */ - - int h_offset; /* horizontal offset of processing window */ - int h_pixels; /* number of horizontal pixels to process */ - - int v_max_out; - int h_max_out; -}; - -/* buffer for one video/vbi frame */ -struct saa7146_buf { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - - /* saa7146 specific */ - struct v4l2_pix_format *fmt; - int (*activate)(struct saa7146_dev *dev, - struct saa7146_buf *buf, - struct saa7146_buf *next); - - /* page tables */ - struct saa7146_pgtable pt[3]; -}; - -struct saa7146_dmaqueue { - struct saa7146_dev *dev; - struct saa7146_buf *curr; - struct list_head queue; - struct timer_list timeout; -}; - -struct saa7146_overlay { - struct saa7146_fh *fh; - struct v4l2_window win; - struct v4l2_clip clips[16]; - int nclips; -}; - -/* per open data */ -struct saa7146_fh { - /* Must be the first field! */ - struct v4l2_fh fh; - struct saa7146_dev *dev; - - /* video capture */ - struct videobuf_queue video_q; - - /* vbi capture */ - struct videobuf_queue vbi_q; - - unsigned int resources; /* resource management for device open */ -}; - -#define STATUS_OVERLAY 0x01 -#define STATUS_CAPTURE 0x02 - -struct saa7146_vv -{ - /* vbi capture */ - struct saa7146_dmaqueue vbi_dmaq; - struct v4l2_vbi_format vbi_fmt; - struct timer_list vbi_read_timeout; - struct file *vbi_read_timeout_file; - /* vbi workaround interrupt queue */ - wait_queue_head_t vbi_wq; - int vbi_fieldcount; - struct saa7146_fh *vbi_streaming; - - int video_status; - struct saa7146_fh *video_fh; - - /* video overlay */ - struct saa7146_overlay ov; - struct v4l2_framebuffer ov_fb; - struct saa7146_format *ov_fmt; - struct saa7146_fh *ov_suspend; - - /* video capture */ - struct saa7146_dmaqueue video_dmaq; - struct v4l2_pix_format video_fmt; - enum v4l2_field last_field; - - /* common: fixme? shouldn't this be in saa7146_fh? - (this leads to a more complicated question: shall the driver - store the different settings (for example S_INPUT) for every open - and restore it appropriately, or should all settings be common for - all opens? currently, we do the latter, like all other - drivers do... */ - struct saa7146_standard *standard; - - int vflip; - int hflip; - int current_hps_source; - int current_hps_sync; - - struct saa7146_dma d_clipping; /* pointer to clipping memory */ - - unsigned int resources; /* resource management for device */ -}; - -/* flags */ -#define SAA7146_USE_PORT_B_FOR_VBI 0x2 /* use input port b for vbi hardware bug workaround */ - -struct saa7146_ext_vv -{ - /* information about the video capabilities of the device */ - int inputs; - int audios; - u32 capabilities; - int flags; - - /* additionally supported transmission standards */ - struct saa7146_standard *stds; - int num_stds; - int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *); - - /* the extension can override this */ - struct v4l2_ioctl_ops vid_ops; - struct v4l2_ioctl_ops vbi_ops; - /* pointer to the saa7146 core ops */ - const struct v4l2_ioctl_ops *core_ops; - - struct v4l2_file_operations vbi_fops; -}; - -struct saa7146_use_ops { - void (*init)(struct saa7146_dev *, struct saa7146_vv *); - int(*open)(struct saa7146_dev *, struct file *); - void (*release)(struct saa7146_dev *, struct file *); - void (*irq_done)(struct saa7146_dev *, unsigned long status); - ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); -}; - -/* from saa7146_fops.c */ -int saa7146_register_device(struct video_device *vid, struct saa7146_dev *dev, char *name, int type); -int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev); -void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state); -void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); -int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); -void saa7146_buffer_timeout(struct timer_list *t); -void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, - struct saa7146_buf *buf); - -int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv); -int saa7146_vv_release(struct saa7146_dev* dev); - -/* from saa7146_hlp.c */ -int saa7146_enable_overlay(struct saa7146_fh *fh); -void saa7146_disable_overlay(struct saa7146_fh *fh); - -void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next); -void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) ; -void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sync); -void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); - -/* from saa7146_video.c */ -extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; -extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; -extern const struct saa7146_use_ops saa7146_video_uops; -int saa7146_start_preview(struct saa7146_fh *fh); -int saa7146_stop_preview(struct saa7146_fh *fh); -long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); -int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); - -/* from saa7146_vbi.c */ -extern const struct saa7146_use_ops saa7146_vbi_uops; - -/* resource management functions */ -int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit); -void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits); - -#define RESOURCE_DMA1_HPS 0x1 -#define RESOURCE_DMA2_CLP 0x2 -#define RESOURCE_DMA3_BRS 0x4 - -/* saa7146 source inputs */ -#define SAA7146_HPS_SOURCE_PORT_A 0x00 -#define SAA7146_HPS_SOURCE_PORT_B 0x01 -#define SAA7146_HPS_SOURCE_YPB_CPA 0x02 -#define SAA7146_HPS_SOURCE_YPA_CPB 0x03 - -/* sync inputs */ -#define SAA7146_HPS_SYNC_PORT_A 0x00 -#define SAA7146_HPS_SYNC_PORT_B 0x01 - -/* some memory sizes */ -/* max. 16 clipping rectangles */ -#define SAA7146_CLIPPING_MEM (16 * 4 * sizeof(u32)) - -/* some defines for the various clipping-modes */ -#define SAA7146_CLIPPING_RECT 0x4 -#define SAA7146_CLIPPING_RECT_INVERTED 0x5 -#define SAA7146_CLIPPING_MASK 0x6 -#define SAA7146_CLIPPING_MASK_INVERTED 0x7 - -/* output formats: each entry holds four information */ -#define RGB08_COMPOSED 0x0217 /* composed is used in the sense of "not-planar" */ -/* this means: planar?=0, yuv2rgb-conversation-mode=2, dither=yes(=1), format-mode = 7 */ -#define RGB15_COMPOSED 0x0213 -#define RGB16_COMPOSED 0x0210 -#define RGB24_COMPOSED 0x0201 -#define RGB32_COMPOSED 0x0202 - -#define Y8 0x0006 -#define YUV411_COMPOSED 0x0003 -#define YUV422_COMPOSED 0x0000 -/* this means: planar?=1, yuv2rgb-conversion-mode=0, dither=no(=0), format-mode = b */ -#define YUV411_DECOMPOSED 0x100b -#define YUV422_DECOMPOSED 0x1009 -#define YUV420_DECOMPOSED 0x100a - -#define IS_PLANAR(x) (x & 0xf000) - -/* misc defines */ -#define SAA7146_NO_SWAP (0x0) -#define SAA7146_TWO_BYTE_SWAP (0x1) -#define SAA7146_FOUR_BYTE_SWAP (0x2) - -#endif -- cgit v1.3-14-g43fede From 3e9ad662e34eb2d42ddef5a2883abd34461dfd9a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 11 Aug 2022 11:17:49 +0200 Subject: media: av7110: move to staging/media/deprecated/saa7146 The av7110 driver depends on saa7146, so move it there. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/av7110/Kconfig | 94 - drivers/staging/media/av7110/Makefile | 23 - drivers/staging/media/av7110/TODO | 3 - .../av7110/audio-bilingual-channel-select.rst | 58 - .../staging/media/av7110/audio-channel-select.rst | 57 - .../staging/media/av7110/audio-clear-buffer.rst | 48 - drivers/staging/media/av7110/audio-continue.rst | 48 - drivers/staging/media/av7110/audio-fclose.rst | 51 - drivers/staging/media/av7110/audio-fopen.rst | 103 - drivers/staging/media/av7110/audio-fwrite.rst | 79 - .../media/av7110/audio-get-capabilities.rst | 54 - drivers/staging/media/av7110/audio-get-status.rst | 54 - drivers/staging/media/av7110/audio-pause.rst | 49 - drivers/staging/media/av7110/audio-play.rst | 48 - .../staging/media/av7110/audio-select-source.rst | 56 - drivers/staging/media/av7110/audio-set-av-sync.rst | 58 - .../staging/media/av7110/audio-set-bypass-mode.rst | 62 - drivers/staging/media/av7110/audio-set-id.rst | 59 - drivers/staging/media/av7110/audio-set-mixer.rst | 53 - drivers/staging/media/av7110/audio-set-mute.rst | 62 - .../staging/media/av7110/audio-set-streamtype.rst | 66 - drivers/staging/media/av7110/audio-stop.rst | 48 - drivers/staging/media/av7110/audio.rst | 27 - drivers/staging/media/av7110/audio_data_types.rst | 116 - .../staging/media/av7110/audio_function_calls.rst | 30 - drivers/staging/media/av7110/av7110.c | 2919 -------------------- drivers/staging/media/av7110/av7110.h | 315 --- drivers/staging/media/av7110/av7110_av.c | 1681 ----------- drivers/staging/media/av7110/av7110_av.h | 32 - drivers/staging/media/av7110/av7110_ca.c | 380 --- drivers/staging/media/av7110/av7110_ca.h | 15 - drivers/staging/media/av7110/av7110_hw.c | 1204 -------- drivers/staging/media/av7110/av7110_hw.h | 496 ---- drivers/staging/media/av7110/av7110_ipack.c | 404 --- drivers/staging/media/av7110/av7110_ipack.h | 13 - drivers/staging/media/av7110/av7110_ir.c | 158 -- drivers/staging/media/av7110/av7110_v4l.c | 952 ------- drivers/staging/media/av7110/budget-patch.c | 665 ----- drivers/staging/media/av7110/dvb_filter.c | 115 - drivers/staging/media/av7110/dvb_filter.h | 242 -- drivers/staging/media/av7110/sp8870.c | 609 ---- drivers/staging/media/av7110/sp8870.h | 37 - .../staging/media/av7110/video-clear-buffer.rst | 54 - drivers/staging/media/av7110/video-command.rst | 96 - drivers/staging/media/av7110/video-continue.rst | 57 - .../staging/media/av7110/video-fast-forward.rst | 72 - drivers/staging/media/av7110/video-fclose.rst | 51 - drivers/staging/media/av7110/video-fopen.rst | 111 - drivers/staging/media/av7110/video-freeze.rst | 61 - drivers/staging/media/av7110/video-fwrite.rst | 79 - .../media/av7110/video-get-capabilities.rst | 61 - drivers/staging/media/av7110/video-get-event.rst | 105 - .../staging/media/av7110/video-get-frame-count.rst | 65 - drivers/staging/media/av7110/video-get-pts.rst | 69 - drivers/staging/media/av7110/video-get-size.rst | 69 - drivers/staging/media/av7110/video-get-status.rst | 72 - drivers/staging/media/av7110/video-play.rst | 57 - .../staging/media/av7110/video-select-source.rst | 76 - drivers/staging/media/av7110/video-set-blank.rst | 64 - .../media/av7110/video-set-display-format.rst | 60 - drivers/staging/media/av7110/video-set-format.rst | 82 - .../staging/media/av7110/video-set-streamtype.rst | 61 - drivers/staging/media/av7110/video-slowmotion.rst | 72 - .../staging/media/av7110/video-stillpicture.rst | 61 - drivers/staging/media/av7110/video-stop.rst | 74 - drivers/staging/media/av7110/video-try-command.rst | 66 - drivers/staging/media/av7110/video.rst | 36 - .../staging/media/av7110/video_function_calls.rst | 35 - drivers/staging/media/av7110/video_types.rst | 248 -- drivers/staging/media/deprecated/saa7146/Kconfig | 1 + drivers/staging/media/deprecated/saa7146/Makefile | 2 +- .../media/deprecated/saa7146/av7110/Kconfig | 106 + .../media/deprecated/saa7146/av7110/Makefile | 23 + .../staging/media/deprecated/saa7146/av7110/TODO | 9 + .../av7110/audio-bilingual-channel-select.rst | 58 + .../saa7146/av7110/audio-channel-select.rst | 57 + .../saa7146/av7110/audio-clear-buffer.rst | 48 + .../deprecated/saa7146/av7110/audio-continue.rst | 48 + .../deprecated/saa7146/av7110/audio-fclose.rst | 51 + .../deprecated/saa7146/av7110/audio-fopen.rst | 103 + .../deprecated/saa7146/av7110/audio-fwrite.rst | 79 + .../saa7146/av7110/audio-get-capabilities.rst | 54 + .../deprecated/saa7146/av7110/audio-get-status.rst | 54 + .../deprecated/saa7146/av7110/audio-pause.rst | 49 + .../media/deprecated/saa7146/av7110/audio-play.rst | 48 + .../saa7146/av7110/audio-select-source.rst | 56 + .../saa7146/av7110/audio-set-av-sync.rst | 58 + .../saa7146/av7110/audio-set-bypass-mode.rst | 62 + .../deprecated/saa7146/av7110/audio-set-id.rst | 59 + .../deprecated/saa7146/av7110/audio-set-mixer.rst | 53 + .../deprecated/saa7146/av7110/audio-set-mute.rst | 62 + .../saa7146/av7110/audio-set-streamtype.rst | 66 + .../media/deprecated/saa7146/av7110/audio-stop.rst | 48 + .../media/deprecated/saa7146/av7110/audio.rst | 27 + .../deprecated/saa7146/av7110/audio_data_types.rst | 116 + .../saa7146/av7110/audio_function_calls.rst | 30 + .../media/deprecated/saa7146/av7110/av7110.c | 2919 ++++++++++++++++++++ .../media/deprecated/saa7146/av7110/av7110.h | 315 +++ .../media/deprecated/saa7146/av7110/av7110_av.c | 1681 +++++++++++ .../media/deprecated/saa7146/av7110/av7110_av.h | 32 + .../media/deprecated/saa7146/av7110/av7110_ca.c | 380 +++ .../media/deprecated/saa7146/av7110/av7110_ca.h | 15 + .../media/deprecated/saa7146/av7110/av7110_hw.c | 1204 ++++++++ .../media/deprecated/saa7146/av7110/av7110_hw.h | 496 ++++ .../media/deprecated/saa7146/av7110/av7110_ipack.c | 404 +++ .../media/deprecated/saa7146/av7110/av7110_ipack.h | 13 + .../media/deprecated/saa7146/av7110/av7110_ir.c | 158 ++ .../media/deprecated/saa7146/av7110/av7110_v4l.c | 952 +++++++ .../media/deprecated/saa7146/av7110/budget-patch.c | 665 +++++ .../media/deprecated/saa7146/av7110/dvb_filter.c | 115 + .../media/deprecated/saa7146/av7110/dvb_filter.h | 242 ++ .../media/deprecated/saa7146/av7110/sp8870.c | 609 ++++ .../media/deprecated/saa7146/av7110/sp8870.h | 37 + .../saa7146/av7110/video-clear-buffer.rst | 54 + .../deprecated/saa7146/av7110/video-command.rst | 96 + .../deprecated/saa7146/av7110/video-continue.rst | 57 + .../saa7146/av7110/video-fast-forward.rst | 72 + .../deprecated/saa7146/av7110/video-fclose.rst | 51 + .../deprecated/saa7146/av7110/video-fopen.rst | 111 + .../deprecated/saa7146/av7110/video-freeze.rst | 61 + .../deprecated/saa7146/av7110/video-fwrite.rst | 79 + .../saa7146/av7110/video-get-capabilities.rst | 61 + .../deprecated/saa7146/av7110/video-get-event.rst | 105 + .../saa7146/av7110/video-get-frame-count.rst | 65 + .../deprecated/saa7146/av7110/video-get-pts.rst | 69 + .../deprecated/saa7146/av7110/video-get-size.rst | 69 + .../deprecated/saa7146/av7110/video-get-status.rst | 72 + .../media/deprecated/saa7146/av7110/video-play.rst | 57 + .../saa7146/av7110/video-select-source.rst | 76 + .../deprecated/saa7146/av7110/video-set-blank.rst | 64 + .../saa7146/av7110/video-set-display-format.rst | 60 + .../deprecated/saa7146/av7110/video-set-format.rst | 82 + .../saa7146/av7110/video-set-streamtype.rst | 61 + .../deprecated/saa7146/av7110/video-slowmotion.rst | 72 + .../saa7146/av7110/video-stillpicture.rst | 61 + .../media/deprecated/saa7146/av7110/video-stop.rst | 74 + .../saa7146/av7110/video-try-command.rst | 66 + .../media/deprecated/saa7146/av7110/video.rst | 36 + .../saa7146/av7110/video_function_calls.rst | 35 + .../deprecated/saa7146/av7110/video_types.rst | 248 ++ 142 files changed, 13677 insertions(+), 13661 deletions(-) delete mode 100644 drivers/staging/media/av7110/Kconfig delete mode 100644 drivers/staging/media/av7110/Makefile delete mode 100644 drivers/staging/media/av7110/TODO delete mode 100644 drivers/staging/media/av7110/audio-bilingual-channel-select.rst delete mode 100644 drivers/staging/media/av7110/audio-channel-select.rst delete mode 100644 drivers/staging/media/av7110/audio-clear-buffer.rst delete mode 100644 drivers/staging/media/av7110/audio-continue.rst delete mode 100644 drivers/staging/media/av7110/audio-fclose.rst delete mode 100644 drivers/staging/media/av7110/audio-fopen.rst delete mode 100644 drivers/staging/media/av7110/audio-fwrite.rst delete mode 100644 drivers/staging/media/av7110/audio-get-capabilities.rst delete mode 100644 drivers/staging/media/av7110/audio-get-status.rst delete mode 100644 drivers/staging/media/av7110/audio-pause.rst delete mode 100644 drivers/staging/media/av7110/audio-play.rst delete mode 100644 drivers/staging/media/av7110/audio-select-source.rst delete mode 100644 drivers/staging/media/av7110/audio-set-av-sync.rst delete mode 100644 drivers/staging/media/av7110/audio-set-bypass-mode.rst delete mode 100644 drivers/staging/media/av7110/audio-set-id.rst delete mode 100644 drivers/staging/media/av7110/audio-set-mixer.rst delete mode 100644 drivers/staging/media/av7110/audio-set-mute.rst delete mode 100644 drivers/staging/media/av7110/audio-set-streamtype.rst delete mode 100644 drivers/staging/media/av7110/audio-stop.rst delete mode 100644 drivers/staging/media/av7110/audio.rst delete mode 100644 drivers/staging/media/av7110/audio_data_types.rst delete mode 100644 drivers/staging/media/av7110/audio_function_calls.rst delete mode 100644 drivers/staging/media/av7110/av7110.c delete mode 100644 drivers/staging/media/av7110/av7110.h delete mode 100644 drivers/staging/media/av7110/av7110_av.c delete mode 100644 drivers/staging/media/av7110/av7110_av.h delete mode 100644 drivers/staging/media/av7110/av7110_ca.c delete mode 100644 drivers/staging/media/av7110/av7110_ca.h delete mode 100644 drivers/staging/media/av7110/av7110_hw.c delete mode 100644 drivers/staging/media/av7110/av7110_hw.h delete mode 100644 drivers/staging/media/av7110/av7110_ipack.c delete mode 100644 drivers/staging/media/av7110/av7110_ipack.h delete mode 100644 drivers/staging/media/av7110/av7110_ir.c delete mode 100644 drivers/staging/media/av7110/av7110_v4l.c delete mode 100644 drivers/staging/media/av7110/budget-patch.c delete mode 100644 drivers/staging/media/av7110/dvb_filter.c delete mode 100644 drivers/staging/media/av7110/dvb_filter.h delete mode 100644 drivers/staging/media/av7110/sp8870.c delete mode 100644 drivers/staging/media/av7110/sp8870.h delete mode 100644 drivers/staging/media/av7110/video-clear-buffer.rst delete mode 100644 drivers/staging/media/av7110/video-command.rst delete mode 100644 drivers/staging/media/av7110/video-continue.rst delete mode 100644 drivers/staging/media/av7110/video-fast-forward.rst delete mode 100644 drivers/staging/media/av7110/video-fclose.rst delete mode 100644 drivers/staging/media/av7110/video-fopen.rst delete mode 100644 drivers/staging/media/av7110/video-freeze.rst delete mode 100644 drivers/staging/media/av7110/video-fwrite.rst delete mode 100644 drivers/staging/media/av7110/video-get-capabilities.rst delete mode 100644 drivers/staging/media/av7110/video-get-event.rst delete mode 100644 drivers/staging/media/av7110/video-get-frame-count.rst delete mode 100644 drivers/staging/media/av7110/video-get-pts.rst delete mode 100644 drivers/staging/media/av7110/video-get-size.rst delete mode 100644 drivers/staging/media/av7110/video-get-status.rst delete mode 100644 drivers/staging/media/av7110/video-play.rst delete mode 100644 drivers/staging/media/av7110/video-select-source.rst delete mode 100644 drivers/staging/media/av7110/video-set-blank.rst delete mode 100644 drivers/staging/media/av7110/video-set-display-format.rst delete mode 100644 drivers/staging/media/av7110/video-set-format.rst delete mode 100644 drivers/staging/media/av7110/video-set-streamtype.rst delete mode 100644 drivers/staging/media/av7110/video-slowmotion.rst delete mode 100644 drivers/staging/media/av7110/video-stillpicture.rst delete mode 100644 drivers/staging/media/av7110/video-stop.rst delete mode 100644 drivers/staging/media/av7110/video-try-command.rst delete mode 100644 drivers/staging/media/av7110/video.rst delete mode 100644 drivers/staging/media/av7110/video_function_calls.rst delete mode 100644 drivers/staging/media/av7110/video_types.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/Kconfig create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/Makefile create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/TODO create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/sp8870.c create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/sp8870.h create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-command.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-play.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst create mode 100644 drivers/staging/media/deprecated/saa7146/av7110/video_types.rst diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index d78b8c3f0814..56eeecb03b31 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -22,8 +22,6 @@ if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order source "drivers/staging/media/atomisp/Kconfig" -source "drivers/staging/media/av7110/Kconfig" - source "drivers/staging/media/hantro/Kconfig" source "drivers/staging/media/imx/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 3aa5e77b62f8..874df4953729 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -15,6 +15,5 @@ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ -obj-$(CONFIG_DVB_AV7110) += av7110/ obj-y += deprecated/vpfe_capture/ obj-y += deprecated/saa7146/ diff --git a/drivers/staging/media/av7110/Kconfig b/drivers/staging/media/av7110/Kconfig deleted file mode 100644 index 9faf9d2d4001..000000000000 --- a/drivers/staging/media/av7110/Kconfig +++ /dev/null @@ -1,94 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config DVB_AV7110_IR - bool - depends on RC_CORE=y || RC_CORE = DVB_AV7110 - default DVB_AV7110 - -config DVB_AV7110 - tristate "AV7110 cards" - depends on DVB_CORE && PCI && I2C - select TTPCI_EEPROM - select VIDEO_SAA7146_VV - depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT - select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT - select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT - select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - help - Support for SAA7146 and AV7110 based DVB cards as produced - by Fujitsu-Siemens, Technotrend, Hauppauge and others. - - This driver only supports the fullfeatured cards with - onboard MPEG2 decoder. - - This driver needs an external firmware. Please use the script - "/scripts/get_dvb_firmware av7110" to - download/extract it, and then copy it to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). - - Alternatively, you can download the file and use the kernel's - EXTRA_FIRMWARE configuration option to build it into your - kernel image by adding the filename to the EXTRA_FIRMWARE - configuration option string. - - Say Y if you own such a card and want to use it. - -config DVB_AV7110_OSD - bool "AV7110 OSD support" - depends on DVB_AV7110 - default y if DVB_AV7110=y || DVB_AV7110=m - help - The AV7110 firmware provides some code to generate an OnScreenDisplay - on the video output. This is kind of nonstandard and not guaranteed to - be maintained. - - Anyway, some popular DVB software like VDR uses this OSD to render - its menus, so say Y if you want to use this software. - - All other people say N. - -config DVB_BUDGET_PATCH - tristate "AV7110 cards with Budget Patch" - depends on DVB_BUDGET_CORE && I2C - depends on DVB_AV7110 - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT - help - Support for Budget Patch (full TS) modification on - SAA7146+AV7110 based cards (DVB-S cards). This - driver doesn't use onboard MPEG2 decoder. The - card is driven in Budget-only mode. Card is - required to have loaded firmware to tune properly. - Firmware can be loaded by insertion and removal of - standard AV7110 driver prior to loading this - driver. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-patch. - -if DVB_AV7110 - -# Frontend driver that it is used only by AV7110 driver -# While technically independent, it doesn't make sense to keep -# it if we drop support for AV7110, as no other driver will use it. - -config DVB_SP8870 - tristate "Spase sp8870 based" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - A DVB-T tuner module. Say Y when you want to support this frontend. - - This driver needs external firmware. Please use the command - "/scripts/get_dvb_firmware sp8870" to - download/extract it, and then copy it to /usr/lib/hotplug/firmware - or /lib/firmware (depending on configuration of firmware hotplug). - -endif diff --git a/drivers/staging/media/av7110/Makefile b/drivers/staging/media/av7110/Makefile deleted file mode 100644 index c04cd0a59109..000000000000 --- a/drivers/staging/media/av7110/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the AV7110 DVB device driver -# - -dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o \ - av7110_ipack.o dvb_filter.o - -ifdef CONFIG_DVB_AV7110_IR -dvb-ttpci-objs += av7110_ir.o -endif - -obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o - -obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o - -obj-$(CONFIG_DVB_SP8870) += sp8870.o - -ccflags-y += -I $(srctree)/drivers/media/dvb-frontends -ccflags-y += -I $(srctree)/drivers/media/tuners -ccflags-y += -I $(srctree)/drivers/media/common -ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/ttpci -ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/common diff --git a/drivers/staging/media/av7110/TODO b/drivers/staging/media/av7110/TODO deleted file mode 100644 index 60062d8441b3..000000000000 --- a/drivers/staging/media/av7110/TODO +++ /dev/null @@ -1,3 +0,0 @@ -- This driver is too old and relies on a different API. - Drop it from Kernel on a couple of versions. -- Cleanup patches for the drivers here won't be accepted. diff --git a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst b/drivers/staging/media/av7110/audio-bilingual-channel-select.rst deleted file mode 100644 index 33b5363317f1..000000000000 --- a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_BILINGUAL_CHANNEL_SELECT: - -============================== -AUDIO_BILINGUAL_CHANNEL_SELECT -============================== - -Name ----- - -AUDIO_BILINGUAL_CHANNEL_SELECT - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_BILINGUAL_CHANNEL_SELECT - -``int ioctl(int fd, AUDIO_BILINGUAL_CHANNEL_SELECT, struct audio_channel_select *select)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_channel_select_t ch - - - Select the output format of the audio (mono left/right, stereo). - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. It has been replaced -by the V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` control -for MPEG decoders controlled through V4L2. - -This ioctl call asks the Audio Device to select the requested channel -for bilingual streams if possible. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-channel-select.rst b/drivers/staging/media/av7110/audio-channel-select.rst deleted file mode 100644 index 74093df92a68..000000000000 --- a/drivers/staging/media/av7110/audio-channel-select.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CHANNEL_SELECT: - -==================== -AUDIO_CHANNEL_SELECT -==================== - -Name ----- - -AUDIO_CHANNEL_SELECT - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CHANNEL_SELECT - -``int ioctl(int fd, AUDIO_CHANNEL_SELECT, struct audio_channel_select *select)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_channel_select_t ch - - - Select the output format of the audio (mono left/right, stereo). - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` control instead. - -This ioctl call asks the Audio Device to select the requested channel if -possible. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-clear-buffer.rst b/drivers/staging/media/av7110/audio-clear-buffer.rst deleted file mode 100644 index a0ebb0278260..000000000000 --- a/drivers/staging/media/av7110/audio-clear-buffer.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CLEAR_BUFFER: - -================== -AUDIO_CLEAR_BUFFER -================== - -Name ----- - -AUDIO_CLEAR_BUFFER - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CLEAR_BUFFER - -``int ioctl(int fd, AUDIO_CLEAR_BUFFER)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to clear all software and hardware -buffers of the audio decoder device. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-continue.rst b/drivers/staging/media/av7110/audio-continue.rst deleted file mode 100644 index a2e9850f37f2..000000000000 --- a/drivers/staging/media/av7110/audio-continue.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CONTINUE: - -============== -AUDIO_CONTINUE -============== - -Name ----- - -AUDIO_CONTINUE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CONTINUE - -``int ioctl(int fd, AUDIO_CONTINUE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl restarts the decoding and playing process previously paused -with AUDIO_PAUSE command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-fclose.rst b/drivers/staging/media/av7110/audio-fclose.rst deleted file mode 100644 index 77857d578e83..000000000000 --- a/drivers/staging/media/av7110/audio-fclose.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fclose: - -======================== -Digital TV audio close() -======================== - -Name ----- - -Digital TV audio close() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int close(int fd) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This system call closes a previously opened audio device. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/audio-fopen.rst b/drivers/staging/media/av7110/audio-fopen.rst deleted file mode 100644 index 774daaab3bad..000000000000 --- a/drivers/staging/media/av7110/audio-fopen.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fopen: - -======================= -Digital TV audio open() -======================= - -Name ----- - -Digital TV audio open() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int open(const char *deviceName, int flags) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - const char \*deviceName - - - Name of specific audio device. - - - .. row 2 - - - int flags - - - A bit-wise OR of the following flags: - - - .. row 3 - - - - - O_RDONLY read-only access - - - .. row 4 - - - - - O_RDWR read/write access - - - .. row 5 - - - - - O_NONBLOCK open in non-blocking mode - - - .. row 6 - - - - - (blocking mode is the default) - -Description ------------ - -This system call opens a named audio device (e.g. -/dev/dvb/adapter0/audio0) for subsequent use. When an open() call has -succeeded, the device will be ready for use. The significance of -blocking or non-blocking mode is described in the documentation for -functions where there is a difference. It does not affect the semantics -of the open() call itself. A device opened in blocking mode can later be -put into non-blocking mode (and vice versa) using the F_SETFL command -of the fcntl system call. This is a standard system call, documented in -the Linux manual page for fcntl. Only one user can open the Audio Device -in O_RDWR mode. All other attempts to open the device in this mode will -fail, and an error code will be returned. If the Audio Device is opened -in O_RDONLY mode, the only ioctl call that can be used is -AUDIO_GET_STATUS. All other call will return with an error code. - -Return Value ------------- - -.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``ENODEV`` - - - Device driver not loaded/available. - - - .. row 2 - - - ``EBUSY`` - - - Device or resource busy. - - - .. row 3 - - - ``EINVAL`` - - - Invalid argument. diff --git a/drivers/staging/media/av7110/audio-fwrite.rst b/drivers/staging/media/av7110/audio-fwrite.rst deleted file mode 100644 index 7b096ac2b6c4..000000000000 --- a/drivers/staging/media/av7110/audio-fwrite.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fwrite: - -========================= -Digital TV audio write() -========================= - -Name ----- - -Digital TV audio write() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: size_t write(int fd, const void *buf, size_t count) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - void \*buf - - - Pointer to the buffer containing the PES data. - - - .. row 3 - - - size_t count - - - Size of buf. - -Description ------------ - -This system call can only be used if AUDIO_SOURCE_MEMORY is selected -in the ioctl call AUDIO_SELECT_SOURCE. The data provided shall be in -PES format. If O_NONBLOCK is not specified the function will block -until buffer space is available. The amount of data to be transferred is -implied by count. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode AUDIO_SOURCE_MEMORY not selected. - - - .. row 2 - - - ``ENOMEM`` - - - Attempted to write more data than the internal buffer can hold. - - - .. row 3 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/audio-get-capabilities.rst b/drivers/staging/media/av7110/audio-get-capabilities.rst deleted file mode 100644 index 6d9eb71dad17..000000000000 --- a/drivers/staging/media/av7110/audio-get-capabilities.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_GET_CAPABILITIES: - -====================== -AUDIO_GET_CAPABILITIES -====================== - -Name ----- - -AUDIO_GET_CAPABILITIES - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_GET_CAPABILITIES - -``int ioctl(int fd, AUDIO_GET_CAPABILITIES, unsigned int *cap)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - unsigned int \*cap - - - Returns a bit array of supported sound formats. - -Description ------------ - -This ioctl call asks the Audio Device to tell us about the decoding -capabilities of the audio hardware. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-get-status.rst b/drivers/staging/media/av7110/audio-get-status.rst deleted file mode 100644 index 7ae8db2e65e9..000000000000 --- a/drivers/staging/media/av7110/audio-get-status.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_GET_STATUS: - -================ -AUDIO_GET_STATUS -================ - -Name ----- - -AUDIO_GET_STATUS - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_GET_STATUS - -``int ioctl(int fd, AUDIO_GET_STATUS, struct audio_status *status)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - struct audio_status \*status - - - Returns the current state of Audio Device. - -Description ------------ - -This ioctl call asks the Audio Device to return the current state of the -Audio Device. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-pause.rst b/drivers/staging/media/av7110/audio-pause.rst deleted file mode 100644 index d37d1ddce4df..000000000000 --- a/drivers/staging/media/av7110/audio-pause.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_PAUSE: - -=========== -AUDIO_PAUSE -=========== - -Name ----- - -AUDIO_PAUSE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_PAUSE - -``int ioctl(int fd, AUDIO_PAUSE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call suspends the audio stream being played. Decoding and -playing are paused. It is then possible to restart again decoding and -playing process of the audio stream using AUDIO_CONTINUE command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-play.rst b/drivers/staging/media/av7110/audio-play.rst deleted file mode 100644 index e591930b6ca7..000000000000 --- a/drivers/staging/media/av7110/audio-play.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_PLAY: - -========== -AUDIO_PLAY -========== - -Name ----- - -AUDIO_PLAY - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_PLAY - -``int ioctl(int fd, AUDIO_PLAY)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to start playing an audio stream -from the selected source. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-select-source.rst b/drivers/staging/media/av7110/audio-select-source.rst deleted file mode 100644 index 6a0c0f365eb1..000000000000 --- a/drivers/staging/media/av7110/audio-select-source.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SELECT_SOURCE: - -=================== -AUDIO_SELECT_SOURCE -=================== - -Name ----- - -AUDIO_SELECT_SOURCE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SELECT_SOURCE - -``int ioctl(int fd, AUDIO_SELECT_SOURCE, struct audio_stream_source *source)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_stream_source_t source - - - Indicates the source that shall be used for the Audio stream. - -Description ------------ - -This ioctl call informs the audio device which source shall be used for -the input data. The possible sources are demux or memory. If -AUDIO_SOURCE_MEMORY is selected, the data is fed to the Audio Device -through the write command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-av-sync.rst b/drivers/staging/media/av7110/audio-set-av-sync.rst deleted file mode 100644 index 85a8016bf025..000000000000 --- a/drivers/staging/media/av7110/audio-set-av-sync.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_AV_SYNC: - -================= -AUDIO_SET_AV_SYNC -================= - -Name ----- - -AUDIO_SET_AV_SYNC - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_AV_SYNC - -``int ioctl(int fd, AUDIO_SET_AV_SYNC, boolean state)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean state - - - Tells the Digital TV subsystem if A/V synchronization shall be ON or OFF. - - TRUE: AV-sync ON - - FALSE: AV-sync OFF - -Description ------------ - -This ioctl call asks the Audio Device to turn ON or OFF A/V -synchronization. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-bypass-mode.rst b/drivers/staging/media/av7110/audio-set-bypass-mode.rst deleted file mode 100644 index 80d551a2053a..000000000000 --- a/drivers/staging/media/av7110/audio-set-bypass-mode.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_BYPASS_MODE: - -===================== -AUDIO_SET_BYPASS_MODE -===================== - -Name ----- - -AUDIO_SET_BYPASS_MODE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_BYPASS_MODE - -``int ioctl(int fd, AUDIO_SET_BYPASS_MODE, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean mode - - - Enables or disables the decoding of the current Audio stream in - the Digital TV subsystem. - - TRUE: Bypass is disabled - - FALSE: Bypass is enabled - -Description ------------ - -This ioctl call asks the Audio Device to bypass the Audio decoder and -forward the stream without decoding. This mode shall be used if streams -that can't be handled by the Digital TV system shall be decoded. Dolby -DigitalTM streams are automatically forwarded by the Digital TV subsystem if -the hardware can handle it. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-id.rst b/drivers/staging/media/av7110/audio-set-id.rst deleted file mode 100644 index 39ad846d412d..000000000000 --- a/drivers/staging/media/av7110/audio-set-id.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_ID: - -============ -AUDIO_SET_ID -============ - -Name ----- - -AUDIO_SET_ID - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_ID - -``int ioctl(int fd, AUDIO_SET_ID, int id)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - int id - - - audio sub-stream id - -Description ------------ - -This ioctl selects which sub-stream is to be decoded if a program or -system stream is sent to the video device. If no audio stream type is -set the id has to be in [0xC0,0xDF] for MPEG sound, in [0x80,0x87] for -AC3 and in [0xA0,0xA7] for LPCM. More specifications may follow for -other stream types. If the stream type is set the id just specifies the -substream id of the audio stream and only the first 5 bits are -recognized. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-mixer.rst b/drivers/staging/media/av7110/audio-set-mixer.rst deleted file mode 100644 index 45dbdf4801e0..000000000000 --- a/drivers/staging/media/av7110/audio-set-mixer.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_MIXER: - -=============== -AUDIO_SET_MIXER -=============== - -Name ----- - -AUDIO_SET_MIXER - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_MIXER - -``int ioctl(int fd, AUDIO_SET_MIXER, struct audio_mixer *mix)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_mixer_t \*mix - - - mixer settings. - -Description ------------ - -This ioctl lets you adjust the mixer settings of the audio decoder. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-mute.rst b/drivers/staging/media/av7110/audio-set-mute.rst deleted file mode 100644 index 987751f92967..000000000000 --- a/drivers/staging/media/av7110/audio-set-mute.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_MUTE: - -============== -AUDIO_SET_MUTE -============== - -Name ----- - -AUDIO_SET_MUTE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_MUTE - -``int ioctl(int fd, AUDIO_SET_MUTE, boolean state)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean state - - - Indicates if audio device shall mute or not. - - TRUE: Audio Mute - - FALSE: Audio Un-mute - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` with the -``V4L2_DEC_CMD_START_MUTE_AUDIO`` flag instead. - -This ioctl call asks the audio device to mute the stream that is -currently being played. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-streamtype.rst b/drivers/staging/media/av7110/audio-set-streamtype.rst deleted file mode 100644 index 77d73c74882f..000000000000 --- a/drivers/staging/media/av7110/audio-set-streamtype.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_STREAMTYPE: - -==================== -AUDIO_SET_STREAMTYPE -==================== - -Name ----- - -AUDIO_SET_STREAMTYPE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_STREAMTYPE - -``int ioctl(fd, AUDIO_SET_STREAMTYPE, int type)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - int type - - - stream type - -Description ------------ - -This ioctl tells the driver which kind of audio stream to expect. This -is useful if the stream offers several audio sub-streams like LPCM and -AC3. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EINVAL`` - - - type is not a valid or supported stream type. diff --git a/drivers/staging/media/av7110/audio-stop.rst b/drivers/staging/media/av7110/audio-stop.rst deleted file mode 100644 index d77f786fd797..000000000000 --- a/drivers/staging/media/av7110/audio-stop.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_STOP: - -========== -AUDIO_STOP -========== - -Name ----- - -AUDIO_STOP - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_STOP - -``int ioctl(int fd, AUDIO_STOP)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to stop playing the current -stream. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio.rst b/drivers/staging/media/av7110/audio.rst deleted file mode 100644 index aa753336b31f..000000000000 --- a/drivers/staging/media/av7110/audio.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _dvb_audio: - -####################### -Digital TV Audio Device -####################### - -The Digital TV audio device controls the MPEG2 audio decoder of the Digital -TV hardware. It can be accessed through ``/dev/dvb/adapter?/audio?``. Data -types and ioctl definitions can be accessed by including -``linux/dvb/audio.h`` in your application. - -Please note that some Digital TV cards don't have their own MPEG decoder, which -results in the omission of the audio and video device. - -These ioctls were also used by V4L2 to control MPEG decoders implemented -in V4L2. The use of these ioctls for that purpose has been made obsolete -and proper V4L2 ioctls or controls have been created to replace that -functionality. - - -.. toctree:: - :maxdepth: 1 - - audio_data_types - audio_function_calls diff --git a/drivers/staging/media/av7110/audio_data_types.rst b/drivers/staging/media/av7110/audio_data_types.rst deleted file mode 100644 index 4744529136a8..000000000000 --- a/drivers/staging/media/av7110/audio_data_types.rst +++ /dev/null @@ -1,116 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _audio_data_types: - -**************** -Audio Data Types -**************** - -This section describes the structures, data types and defines used when -talking to the audio device. - -.. c:type:: audio_stream_source - -The audio stream source is set through the AUDIO_SELECT_SOURCE call -and can take the following values, depending on whether we are replaying -from an internal (demux) or external (user write) source. - - -.. code-block:: c - - typedef enum { - AUDIO_SOURCE_DEMUX, - AUDIO_SOURCE_MEMORY - } audio_stream_source_t; - -AUDIO_SOURCE_DEMUX selects the demultiplexer (fed either by the -frontend or the DVR device) as the source of the video stream. If -AUDIO_SOURCE_MEMORY is selected the stream comes from the application -through the ``write()`` system call. - - -.. c:type:: audio_play_state - -The following values can be returned by the AUDIO_GET_STATUS call -representing the state of audio playback. - - -.. code-block:: c - - typedef enum { - AUDIO_STOPPED, - AUDIO_PLAYING, - AUDIO_PAUSED - } audio_play_state_t; - - -.. c:type:: audio_channel_select - -The audio channel selected via AUDIO_CHANNEL_SELECT is determined by -the following values. - - -.. code-block:: c - - typedef enum { - AUDIO_STEREO, - AUDIO_MONO_LEFT, - AUDIO_MONO_RIGHT, - AUDIO_MONO, - AUDIO_STEREO_SWAPPED - } audio_channel_select_t; - - -.. c:type:: audio_status - -The AUDIO_GET_STATUS call returns the following structure informing -about various states of the playback operation. - - -.. code-block:: c - - typedef struct audio_status { - boolean AV_sync_state; - boolean mute_state; - audio_play_state_t play_state; - audio_stream_source_t stream_source; - audio_channel_select_t channel_select; - boolean bypass_mode; - audio_mixer_t mixer_state; - } audio_status_t; - - -.. c:type:: audio_mixer - -The following structure is used by the AUDIO_SET_MIXER call to set the -audio volume. - - -.. code-block:: c - - typedef struct audio_mixer { - unsigned int volume_left; - unsigned int volume_right; - } audio_mixer_t; - - -.. _audio_encodings: - -audio encodings -=============== - -A call to AUDIO_GET_CAPABILITIES returns an unsigned integer with the -following bits set according to the hardwares capabilities. - - -.. code-block:: c - - #define AUDIO_CAP_DTS 1 - #define AUDIO_CAP_LPCM 2 - #define AUDIO_CAP_MP1 4 - #define AUDIO_CAP_MP2 8 - #define AUDIO_CAP_MP3 16 - #define AUDIO_CAP_AAC 32 - #define AUDIO_CAP_OGG 64 - #define AUDIO_CAP_SDDS 128 - #define AUDIO_CAP_AC3 256 diff --git a/drivers/staging/media/av7110/audio_function_calls.rst b/drivers/staging/media/av7110/audio_function_calls.rst deleted file mode 100644 index fa5ba9539caf..000000000000 --- a/drivers/staging/media/av7110/audio_function_calls.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _audio_function_calls: - -******************** -Audio Function Calls -******************** - -.. toctree:: - :maxdepth: 1 - - audio-fopen - audio-fclose - audio-fwrite - audio-stop - audio-play - audio-pause - audio-continue - audio-select-source - audio-set-mute - audio-set-av-sync - audio-set-bypass-mode - audio-channel-select - audio-bilingual-channel-select - audio-get-status - audio-get-capabilities - audio-clear-buffer - audio-set-id - audio-set-mixer - audio-set-streamtype diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c deleted file mode 100644 index df81a9b744c2..000000000000 --- a/drivers/staging/media/av7110/av7110.c +++ /dev/null @@ -1,2919 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) - * av7110.c: initialization and demux stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * the project's page is at https://linuxtv.org - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - -#include - -#include "ttpci-eeprom.h" -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" -#include "av7110_ca.h" -#include "av7110_ipack.h" - -#include "bsbe1.h" -#include "lnbp21.h" -#include "bsru6.h" - -#define TS_WIDTH 376 -#define TS_HEIGHT 512 -#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) -#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) - - -int av7110_debug; - -static int vidmode = CVBS_RGB_OUT; -static int pids_off; -static int adac = DVB_ADAC_TI; -static int hw_sections; -static int rgb_on; -static int volume = 255; -static int budgetpatch; -static int wss_cfg_4_3 = 0x4008; -static int wss_cfg_16_9 = 0x0007; -static int tv_standard; -static int full_ts; - -module_param_named(debug, av7110_debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); -module_param(vidmode, int, 0444); -MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); -module_param(pids_off, int, 0444); -MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); -module_param(adac, int, 0444); -MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); -module_param(hw_sections, int, 0444); -MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); -module_param(rgb_on, int, 0444); -MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); -module_param(volume, int, 0444); -MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); -module_param(budgetpatch, int, 0444); -MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); -module_param(full_ts, int, 0444); -MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); -module_param(wss_cfg_4_3, int, 0444); -MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); -module_param(wss_cfg_16_9, int, 0444); -MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); -module_param(tv_standard, int, 0444); -MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -static void restart_feeds(struct av7110 *av7110); -static int budget_start_feed(struct dvb_demux_feed *feed); -static int budget_stop_feed(struct dvb_demux_feed *feed); - -static int av7110_num; - -#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ -{\ - if (fe_func != NULL) { \ - av7110_copy = fe_func; \ - fe_func = av7110_func; \ - } \ -} - - -static void init_av7110_av(struct av7110 *av7110) -{ - int ret; - struct saa7146_dev *dev = av7110->dev; - - /* set internal volume control to maximum */ - av7110->adac_type = DVB_ADAC_TI; - ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); - if (ret < 0) - printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) av7110->display_ar); - if (ret < 0) - printk("dvb-ttpci: unable to set aspect ratio\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); - if (ret < 0) - printk("dvb-ttpci: unable to set pan scan\n"); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); - if (ret < 0) - printk("dvb-ttpci: unable to configure 4:3 wss\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); - if (ret < 0) - printk("dvb-ttpci: unable to configure 16:9 wss\n"); - - ret = av7710_set_video_mode(av7110, vidmode); - if (ret < 0) - printk("dvb-ttpci:cannot set video mode:%d\n",ret); - - /* handle different card types */ - /* remaining inits according to card and frontend type */ - av7110->analog_tuner_flags = 0; - av7110->current_input = 0; - if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) - av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on - if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { - printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_CRYSTAL; - i2c_writereg(av7110, 0x20, 0x01, 0xd2); - i2c_writereg(av7110, 0x20, 0x02, 0x49); - i2c_writereg(av7110, 0x20, 0x03, 0x00); - i2c_writereg(av7110, 0x20, 0x04, 0x00); - - /** - * some special handling for the Siemens DVB-C cards... - */ - } else if (0 == av7110_init_analog_module(av7110)) { - /* done. */ - } - else if (dev->pci->subsystem_vendor == 0x110a) { - printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_NONE; - } - else { - av7110->adac_type = adac; - printk("dvb-ttpci: adac type set to %d @ card %d\n", - av7110->adac_type, av7110->dvb_adapter.num); - } - - if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { - // switch DVB SCART on - ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); - if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); - ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); - if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); - if (rgb_on && - ((av7110->dev->pci->subsystem_vendor == 0x110a) || - (av7110->dev->pci->subsystem_vendor == 0x13c2)) && - (av7110->dev->pci->subsystem_device == 0x0000)) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 - //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 - } - } - - if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) - av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on - - ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); - if (ret < 0) - printk("dvb-ttpci:cannot set volume :%d\n",ret); -} - -static void recover_arm(struct av7110 *av7110) -{ - dprintk(4, "%p\n",av7110); - - av7110_bootarm(av7110); - msleep(100); - - init_av7110_av(av7110); - - /* card-specific recovery */ - if (av7110->recover) - av7110->recover(av7110); - - restart_feeds(av7110); - -#if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_set_ir_config(av7110); -#endif -} - -static void av7110_arm_sync(struct av7110 *av7110) -{ - if (av7110->arm_thread) - kthread_stop(av7110->arm_thread); - - av7110->arm_thread = NULL; -} - -static int arm_thread(void *data) -{ - struct av7110 *av7110 = data; - u16 newloops = 0; - int timeout; - - dprintk(4, "%p\n",av7110); - - for (;;) { - timeout = wait_event_interruptible_timeout(av7110->arm_wait, - kthread_should_stop(), 5 * HZ); - - if (-ERESTARTSYS == timeout || kthread_should_stop()) { - /* got signal or told to quit*/ - break; - } - - if (!av7110->arm_ready) - continue; - - if (mutex_lock_interruptible(&av7110->dcomlock)) - break; - newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); - mutex_unlock(&av7110->dcomlock); - - if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { - printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", - av7110->dvb_adapter.num); - - recover_arm(av7110); - - if (mutex_lock_interruptible(&av7110->dcomlock)) - break; - newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; - mutex_unlock(&av7110->dcomlock); - } - av7110->arm_loops = newloops; - av7110->arm_errors = 0; - } - - return 0; -} - - -/**************************************************************************** - * IRQ handling - ****************************************************************************/ - -static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, - u8 *buffer2, size_t buffer2_len, - struct dvb_demux_filter *dvbdmxfilter, - struct av7110 *av7110) -{ - if (!dvbdmxfilter->feed->demux->dmx.frontend) - return 0; - if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) - return 0; - - switch (dvbdmxfilter->type) { - case DMX_TYPE_SEC: - if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) - return 0; - if (dvbdmxfilter->doneq) { - struct dmx_section_filter *filter = &dvbdmxfilter->filter; - int i; - u8 xor, neq = 0; - - for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { - xor = filter->filter_value[i] ^ buffer1[i]; - neq |= dvbdmxfilter->maskandnotmode[i] & xor; - } - if (!neq) - return 0; - } - return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, - buffer2, buffer2_len, - &dvbdmxfilter->filter, NULL); - case DMX_TYPE_TS: - if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) - return 0; - if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) - return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, - buffer2, buffer2_len, - &dvbdmxfilter->feed->feed.ts, - NULL); - else - av7110_p2t_write(buffer1, buffer1_len, - dvbdmxfilter->feed->pid, - &av7110->p2t_filter[dvbdmxfilter->index]); - return 0; - default: - return 0; - } -} - - -//#define DEBUG_TIMING -static inline void print_time(char *s) -{ -#ifdef DEBUG_TIMING - struct timespec64 ts; - ktime_get_real_ts64(&ts); - printk("%s: %lld.%09ld\n", s, (s64)ts.tv_sec, ts.tv_nsec); -#endif -} - -#define DEBI_READ 0 -#define DEBI_WRITE 1 -static inline void start_debi_dma(struct av7110 *av7110, int dir, - unsigned long addr, unsigned int len) -{ - dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); - if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); - return; - } - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ - SAA7146_IER_ENABLE(av7110->dev, MASK_19); - if (len < 5) - len = 5; /* we want a real DEBI DMA */ - if (dir == DEBI_WRITE) - iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); - else - irdebi(av7110, DEBISWAB, addr, 0, len); -} - -static void debiirq(struct tasklet_struct *t) -{ - struct av7110 *av7110 = from_tasklet(av7110, t, debi_tasklet); - int type = av7110->debitype; - int handle = (type >> 8) & 0x1f; - unsigned int xfer = 0; - - print_time("debi"); - dprintk(4, "type 0x%04x\n", type); - - if (type == -1) { - printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); - goto debi_done; - } - av7110->debitype = -1; - - switch (type & 0xff) { - - case DATA_TS_RECORD: - dvb_dmx_swfilter_packets(&av7110->demux, - (const u8 *) av7110->debi_virt, - av7110->debilen / 188); - xfer = RX_BUFF; - break; - - case DATA_PES_RECORD: - if (av7110->demux.recording) - av7110_record_cb(&av7110->p2t[handle], - (u8 *) av7110->debi_virt, - av7110->debilen); - xfer = RX_BUFF; - break; - - case DATA_IPMPE: - case DATA_FSECTION: - case DATA_PIPING: - if (av7110->handle2filter[handle]) - DvbDmxFilterCallback((u8 *)av7110->debi_virt, - av7110->debilen, NULL, 0, - av7110->handle2filter[handle], - av7110); - xfer = RX_BUFF; - break; - - case DATA_CI_GET: - { - u8 *data = av7110->debi_virt; - u8 data_0 = data[0]; - - if (data_0 < 2 && data[2] == 0xff) { - int flags = 0; - if (data[5] > 0) - flags |= CA_CI_MODULE_PRESENT; - if (data[5] > 5) - flags |= CA_CI_MODULE_READY; - av7110->ci_slot[data_0].flags = flags; - } else - ci_get_data(&av7110->ci_rbuffer, - av7110->debi_virt, - av7110->debilen); - xfer = RX_BUFF; - break; - } - - case DATA_COMMON_INTERFACE: - CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); - xfer = RX_BUFF; - break; - - case DATA_DEBUG_MESSAGE: - ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; - printk("%s\n", (s8 *) av7110->debi_virt); - xfer = RX_BUFF; - break; - - case DATA_CI_PUT: - dprintk(4, "debi DATA_CI_PUT\n"); - xfer = TX_BUFF; - break; - case DATA_MPEG_PLAY: - dprintk(4, "debi DATA_MPEG_PLAY\n"); - xfer = TX_BUFF; - break; - case DATA_BMP_LOAD: - dprintk(4, "debi DATA_BMP_LOAD\n"); - xfer = TX_BUFF; - break; - default: - break; - } -debi_done: - spin_lock(&av7110->debilock); - if (xfer) - iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); - ARM_ClearMailBox(av7110); - spin_unlock(&av7110->debilock); -} - -/* irq from av7110 firmware writing the mailbox register in the DPRAM */ -static void gpioirq(struct tasklet_struct *t) -{ - struct av7110 *av7110 = from_tasklet(av7110, t, gpio_tasklet); - u32 rxbuf, txbuf; - int len; - - if (av7110->debitype != -1) - /* we shouldn't get any irq while a debi xfer is running */ - printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); - - if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); - BUG(); /* maybe we should try resetting the debi? */ - } - - spin_lock(&av7110->debilock); - ARM_ClearIrq(av7110); - - /* see what the av7110 wants */ - av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); - av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - len = (av7110->debilen + 3) & ~3; - - print_time("gpio"); - dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); - - switch (av7110->debitype & 0xff) { - - case DATA_TS_PLAY: - case DATA_PES_PLAY: - break; - - case DATA_MPEG_VIDEO_EVENT: - { - u32 h_ar; - struct video_event event; - - av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); - h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); - - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - - av7110->video_size.h = h_ar & 0xfff; - - event.type = VIDEO_EVENT_SIZE_CHANGED; - event.u.size.w = av7110->video_size.w; - event.u.size.h = av7110->video_size.h; - switch ((h_ar >> 12) & 0xf) - { - case 3: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; - event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; - av7110->videostate.video_format = VIDEO_FORMAT_16_9; - break; - case 4: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; - event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; - av7110->videostate.video_format = VIDEO_FORMAT_221_1; - break; - default: - av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; - event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; - av7110->videostate.video_format = VIDEO_FORMAT_4_3; - } - - dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", - av7110->video_size.w, av7110->video_size.h, - av7110->video_size.aspect_ratio); - - dvb_video_add_event(av7110, &event); - break; - } - - case DATA_CI_PUT: - { - int avail; - struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; - - avail = dvb_ringbuffer_avail(cibuf); - if (avail <= 2) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; - len |= DVB_RINGBUFFER_PEEK(cibuf, 1); - if (avail < len + 2) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - DVB_RINGBUFFER_SKIP(cibuf, 2); - - dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); - - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - dprintk(8, "DMA: CI\n"); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); - spin_unlock(&av7110->debilock); - wake_up(&cibuf->queue); - return; - } - - case DATA_MPEG_PLAY: - if (!av7110->playing) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - len = 0; - if (av7110->debitype & 0x100) { - spin_lock(&av7110->aout.lock); - len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); - spin_unlock(&av7110->aout.lock); - } - if (len <= 0 && (av7110->debitype & 0x200) - &&av7110->videostate.play_state != VIDEO_FREEZED) { - spin_lock(&av7110->avout.lock); - len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); - spin_unlock(&av7110->avout.lock); - } - if (len <= 0) { - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - break; - } - dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - dprintk(8, "DMA: MPEG_PLAY\n"); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_BMP_LOAD: - len = av7110->debilen; - dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); - if (!len) { - av7110->bmp_state = BMP_LOADED; - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); - iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); - wake_up(&av7110->bmpq); - dprintk(8, "gpio DATA_BMP_LOAD done\n"); - break; - } - if (len > av7110->bmplen) - len = av7110->bmplen; - if (len > 2 * 1024) - len = 2 * 1024; - iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); - iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); - av7110->bmpp += len; - av7110->bmplen -= len; - dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_CI_GET: - case DATA_COMMON_INTERFACE: - case DATA_FSECTION: - case DATA_IPMPE: - case DATA_PIPING: - if (!len || len > 4 * 1024) { - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - } - fallthrough; - - case DATA_TS_RECORD: - case DATA_PES_RECORD: - dprintk(8, "DMA: TS_REC etc.\n"); - start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_DEBUG_MESSAGE: - if (!len || len > 0xff) { - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - } - start_debi_dma(av7110, DEBI_READ, Reserved, len); - spin_unlock(&av7110->debilock); - return; - - case DATA_IRCOMMAND: -#if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_ir_handler(av7110, - swahw32(irdebi(av7110, DEBINOSWAP, Reserved, - 0, 4))); -#endif - iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); - break; - - default: - printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", - av7110->debitype, av7110->debilen); - break; - } - av7110->debitype = -1; - ARM_ClearMailBox(av7110); - spin_unlock(&av7110->debilock); -} - - -#ifdef CONFIG_DVB_AV7110_OSD -static int dvb_osd_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(4, "%p\n", av7110); - - if (cmd == OSD_SEND_CMD) - return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); - if (cmd == OSD_GET_CAPABILITY) - return av7110_osd_capability(av7110, (osd_cap_t *) parg); - - return -EINVAL; -} - - -static const struct file_operations dvb_osd_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_generic_open, - .release = dvb_generic_release, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_osd = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_osd_fops, - .kernel_ioctl = dvb_osd_ioctl, -}; -#endif /* CONFIG_DVB_AV7110_OSD */ - - -static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid) -{ - u16 aflags = 0; - - dprintk(4, "%p\n", av7110); - - if (vpid == 0x1fff || apid == 0x1fff || - ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { - vpid = apid = ttpid = subpid = pcrpid = 0; - av7110->pids[DMX_PES_VIDEO] = 0; - av7110->pids[DMX_PES_AUDIO] = 0; - av7110->pids[DMX_PES_TELETEXT] = 0; - av7110->pids[DMX_PES_PCR] = 0; - } - - if (av7110->audiostate.bypass_mode) - aflags |= 0x8000; - - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, - pcrpid, vpid, apid, ttpid, subpid, aflags); -} - -int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid) -{ - int ret = 0; - dprintk(4, "%p\n", av7110); - - if (mutex_lock_interruptible(&av7110->pid_mutex)) - return -ERESTARTSYS; - - if (!(vpid & 0x8000)) - av7110->pids[DMX_PES_VIDEO] = vpid; - if (!(apid & 0x8000)) - av7110->pids[DMX_PES_AUDIO] = apid; - if (!(ttpid & 0x8000)) - av7110->pids[DMX_PES_TELETEXT] = ttpid; - if (!(pcrpid & 0x8000)) - av7110->pids[DMX_PES_PCR] = pcrpid; - - av7110->pids[DMX_PES_SUBTITLE] = 0; - - if (av7110->fe_synced) { - pcrpid = av7110->pids[DMX_PES_PCR]; - ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); - } - - mutex_unlock(&av7110->pid_mutex); - return ret; -} - - -/****************************************************************************** - * hardware filter functions - ******************************************************************************/ - -static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) -{ - struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; - struct av7110 *av7110 = dvbdmxfeed->demux->priv; - u16 buf[20]; - int ret, i; - u16 handle; -// u16 mode = 0x0320; - u16 mode = 0xb96a; - - dprintk(4, "%p\n", av7110); - - if (av7110->full_ts) - return 0; - - if (dvbdmxfilter->type == DMX_TYPE_SEC) { - if (hw_sections) { - buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | - dvbdmxfilter->maskandmode[0]; - for (i = 3; i < 18; i++) - buf[i + 4 - 2] = - (dvbdmxfilter->filter.filter_value[i] << 8) | - dvbdmxfilter->maskandmode[i]; - mode = 4; - } - } else if ((dvbdmxfeed->ts_type & TS_PACKET) && - !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { - av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); - } - - buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; - buf[1] = 16; - buf[2] = dvbdmxfeed->pid; - buf[3] = mode; - - ret = av7110_fw_request(av7110, buf, 20, &handle, 1); - if (ret != 0 || handle >= 32) { - printk(KERN_ERR "dvb-ttpci: %s error buf %04x %04x %04x %04x ret %d handle %04x\n", - __func__, buf[0], buf[1], buf[2], buf[3], - ret, handle); - dvbdmxfilter->hw_handle = 0xffff; - if (!ret) - ret = -1; - return ret; - } - - av7110->handle2filter[handle] = dvbdmxfilter; - dvbdmxfilter->hw_handle = handle; - - return ret; -} - -static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) -{ - struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; - u16 buf[3]; - u16 answ[2]; - int ret; - u16 handle; - - dprintk(4, "%p\n", av7110); - - if (av7110->full_ts) - return 0; - - handle = dvbdmxfilter->hw_handle; - if (handle >= 32) { - printk("%s tried to stop invalid filter %04x, filter type = %x\n", - __func__, handle, dvbdmxfilter->type); - return -EINVAL; - } - - av7110->handle2filter[handle] = NULL; - - buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; - buf[1] = 1; - buf[2] = handle; - ret = av7110_fw_request(av7110, buf, 3, answ, 2); - if (ret != 0 || answ[1] != handle) { - printk(KERN_ERR "dvb-ttpci: %s error cmd %04x %04x %04x ret %x resp %04x %04x pid %d\n", - __func__, buf[0], buf[1], buf[2], ret, - answ[0], answ[1], dvbdmxfilter->feed->pid); - if (!ret) - ret = -1; - } - return ret; -} - - -static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = dvbdmx->priv; - u16 *pid = dvbdmx->pids, npids[5]; - int i; - int ret = 0; - - dprintk(4, "%p\n", av7110); - - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; - i = dvbdmxfeed->pes_type; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; - if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { - npids[i] = 0; - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - if (!ret) - ret = StartHWFilter(dvbdmxfeed->filter); - return ret; - } - if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - if (ret) - return ret; - } - - if (dvbdmxfeed->pes_type < 2 && npids[0]) - if (av7110->fe_synced) - { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - if (ret) - return ret; - } - - if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { - if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) - ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); - if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) - ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); - } - return ret; -} - -static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = dvbdmx->priv; - u16 *pid = dvbdmx->pids, npids[5]; - int i; - - int ret = 0; - - dprintk(4, "%p\n", av7110); - - if (dvbdmxfeed->pes_type <= 1) { - ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); - if (ret) - return ret; - if (!av7110->rec_mode) - dvbdmx->recording = 0; - if (!av7110->playing) - dvbdmx->playing = 0; - } - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; - i = dvbdmxfeed->pes_type; - switch (i) { - case 2: //teletext - if (dvbdmxfeed->ts_type & TS_PACKET) - ret = StopHWFilter(dvbdmxfeed->filter); - npids[2] = 0; - break; - case 0: - case 1: - case 4: - if (!pids_off) - return 0; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; - break; - } - if (!ret) - ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); - return ret; -} - -static int av7110_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = demux->priv; - int ret = 0; - - dprintk(4, "%p\n", av7110); - - if (!demux->dmx.frontend) - return -EINVAL; - - if (!av7110->full_ts && feed->pid > 0x1fff) - return -EINVAL; - - if (feed->type == DMX_TYPE_TS) { - if ((feed->ts_type & TS_DECODER) && - (feed->pes_type <= DMX_PES_PCR)) { - switch (demux->dmx.frontend->source) { - case DMX_MEMORY_FE: - if (feed->ts_type & TS_DECODER) - if (feed->pes_type < 2 && - !(demux->pids[0] & 0x8000) && - !(demux->pids[1] & 0x8000)) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - ret = av7110_av_start_play(av7110,RP_AV); - if (!ret) - demux->playing = 1; - } - break; - default: - ret = dvb_feed_start_pid(feed); - break; - } - } else if ((feed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE)) { - ret = StartHWFilter(feed->filter); - } - } - - if (av7110->full_ts) { - budget_start_feed(feed); - return ret; - } - - if (feed->type == DMX_TYPE_SEC) { - int i; - - for (i = 0; i < demux->filternum; i++) { - if (demux->filter[i].state != DMX_STATE_READY) - continue; - if (demux->filter[i].type != DMX_TYPE_SEC) - continue; - if (demux->filter[i].filter.parent != &feed->feed.sec) - continue; - demux->filter[i].state = DMX_STATE_GO; - if (demux->dmx.frontend->source != DMX_MEMORY_FE) { - ret = StartHWFilter(&demux->filter[i]); - if (ret) - break; - } - } - } - - return ret; -} - - -static int av7110_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = demux->priv; - int i, rc, ret = 0; - dprintk(4, "%p\n", av7110); - - if (feed->type == DMX_TYPE_TS) { - if (feed->ts_type & TS_DECODER) { - if (feed->pes_type >= DMX_PES_OTHER || - !demux->pesfilter[feed->pes_type]) - return -EINVAL; - demux->pids[feed->pes_type] |= 0x8000; - demux->pesfilter[feed->pes_type] = NULL; - } - if (feed->ts_type & TS_DECODER && - feed->pes_type < DMX_PES_OTHER) { - ret = dvb_feed_stop_pid(feed); - } else - if ((feed->ts_type & TS_PACKET) && - (demux->dmx.frontend->source != DMX_MEMORY_FE)) - ret = StopHWFilter(feed->filter); - } - - if (av7110->full_ts) { - budget_stop_feed(feed); - return ret; - } - - if (feed->type == DMX_TYPE_SEC) { - for (i = 0; ifilternum; i++) { - if (demux->filter[i].state == DMX_STATE_GO && - demux->filter[i].filter.parent == &feed->feed.sec) { - demux->filter[i].state = DMX_STATE_READY; - if (demux->dmx.frontend->source != DMX_MEMORY_FE) { - rc = StopHWFilter(&demux->filter[i]); - if (!ret) - ret = rc; - /* keep going, stop as many filters as possible */ - } - } - } - } - - return ret; -} - - -static void restart_feeds(struct av7110 *av7110) -{ - struct dvb_demux *dvbdmx = &av7110->demux; - struct dvb_demux_feed *feed; - int mode; - int feeding; - int i, j; - - dprintk(4, "%p\n", av7110); - - mode = av7110->playing; - av7110->playing = 0; - av7110->rec_mode = 0; - - feeding = av7110->feeding1; /* full_ts mod */ - - for (i = 0; i < dvbdmx->feednum; i++) { - feed = &dvbdmx->feed[i]; - if (feed->state == DMX_STATE_GO) { - if (feed->type == DMX_TYPE_SEC) { - for (j = 0; j < dvbdmx->filternum; j++) { - if (dvbdmx->filter[j].type != DMX_TYPE_SEC) - continue; - if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) - continue; - if (dvbdmx->filter[j].state == DMX_STATE_GO) - dvbdmx->filter[j].state = DMX_STATE_READY; - } - } - av7110_start_feed(feed); - } - } - - av7110->feeding1 = feeding; /* full_ts mod */ - - if (mode) - av7110_av_start_play(av7110, mode); -} - -static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, - uint64_t *stc, unsigned int *base) -{ - int ret; - u16 fwstc[4]; - u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); - struct dvb_demux *dvbdemux; - struct av7110 *av7110; - - /* pointer casting paranoia... */ - BUG_ON(!demux); - dvbdemux = demux->priv; - BUG_ON(!dvbdemux); - av7110 = dvbdemux->priv; - - dprintk(4, "%p\n", av7110); - - if (num != 0) - return -EINVAL; - - ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); - if (ret) { - printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); - return ret; - } - dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", - fwstc[0], fwstc[1], fwstc[2], fwstc[3]); - - *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | - (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); - *base = 1; - - dprintk(4, "stc = %lu\n", (unsigned long)*stc); - - return 0; -} - - -/****************************************************************************** - * SEC device file operations - ******************************************************************************/ - - -static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) -{ - struct av7110* av7110 = fe->dvb->priv; - - switch (tone) { - case SEC_TONE_ON: - return Set22K(av7110, 1); - - case SEC_TONE_OFF: - return Set22K(av7110, 0); - - default: - return -EINVAL; - } -} - -static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); -} - -static int av7110_diseqc_send_burst(struct dvb_frontend* fe, - enum fe_sec_mini_cmd minicmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - return av7110_diseqc_send(av7110, 0, NULL, minicmd); -} - -/* simplified code from budget-core.c */ -static int stop_ts_capture(struct av7110 *budget) -{ - dprintk(2, "budget: %p\n", budget); - - if (--budget->feeding1) - return budget->feeding1; - saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ - SAA7146_IER_DISABLE(budget->dev, MASK_10); - SAA7146_ISR_CLEAR(budget->dev, MASK_10); - return 0; -} - -static int start_ts_capture(struct av7110 *budget) -{ - unsigned y; - - dprintk(2, "budget: %p\n", budget); - - if (budget->feeding1) - return ++budget->feeding1; - for (y = 0; y < TS_HEIGHT; y++) - memset(budget->grabbing + y * TS_WIDTH, 0x00, TS_WIDTH); - budget->ttbp = 0; - SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ - SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ - saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ - return ++budget->feeding1; -} - -static int budget_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *budget = demux->priv; - int status; - - dprintk(2, "av7110: %p\n", budget); - - spin_lock(&budget->feedlock1); - feed->pusi_seen = false; /* have a clean section start */ - status = start_ts_capture(budget); - spin_unlock(&budget->feedlock1); - return status; -} - -static int budget_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *budget = demux->priv; - int status; - - dprintk(2, "budget: %p\n", budget); - - spin_lock(&budget->feedlock1); - status = stop_ts_capture(budget); - spin_unlock(&budget->feedlock1); - return status; -} - -static void vpeirq(struct tasklet_struct *t) -{ - struct av7110 *budget = from_tasklet(budget, t, vpe_tasklet); - u8 *mem = (u8 *) (budget->grabbing); - u32 olddma = budget->ttbp; - u32 newdma = saa7146_read(budget->dev, PCI_VDP3); - struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; - - /* nearest lower position divisible by 188 */ - newdma -= newdma % 188; - - if (newdma >= TS_BUFLEN) - return; - - budget->ttbp = newdma; - - if (!budget->feeding1 || (newdma == olddma)) - return; - - /* Ensure streamed PCI data is synced to CPU */ - dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, - budget->pt.nents, DMA_FROM_DEVICE); - -#if 0 - /* track rps1 activity */ - printk("vpeirq: %02x Event Counter 1 0x%04x\n", - mem[olddma], - saa7146_read(budget->dev, EC1R) & 0x3fff); -#endif - - if (newdma > olddma) - /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); - else { - /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); - dvb_dmx_swfilter_packets(demux, mem, newdma / 188); - } -} - -static int av7110_register(struct av7110 *av7110) -{ - int ret, i; - struct dvb_demux *dvbdemux = &av7110->demux; - struct dvb_demux *dvbdemux1 = &av7110->demux1; - - dprintk(4, "%p\n", av7110); - - if (av7110->registered) - return -1; - - av7110->registered = 1; - - dvbdemux->priv = (void *) av7110; - - for (i = 0; i < 32; i++) - av7110->handle2filter[i] = NULL; - - dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; - dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; - dvbdemux->start_feed = av7110_start_feed; - dvbdemux->stop_feed = av7110_stop_feed; - dvbdemux->write_to_decoder = av7110_write_to_decoder; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&av7110->demux); - av7110->demux.dmx.get_stc = dvb_get_stc; - - av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; - av7110->dmxdev.demux = &dvbdemux->dmx; - av7110->dmxdev.capabilities = 0; - - dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); - - av7110->hw_frontend.source = DMX_FRONTEND_0; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); - - if (ret < 0) - return ret; - - av7110->mem_frontend.source = DMX_MEMORY_FE; - - ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); - - if (ret < 0) - return ret; - - ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, - &av7110->hw_frontend); - if (ret < 0) - return ret; - - av7110_av_register(av7110); - av7110_ca_register(av7110); - -#ifdef CONFIG_DVB_AV7110_OSD - dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, - &dvbdev_osd, av7110, DVB_DEVICE_OSD, 0); -#endif - - dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); - - if (budgetpatch) { - /* initialize software demux1 without its own frontend - * demux1 hardware is connected to frontend0 of demux0 - */ - dvbdemux1->priv = (void *) av7110; - - dvbdemux1->filternum = 256; - dvbdemux1->feednum = 256; - dvbdemux1->start_feed = budget_start_feed; - dvbdemux1->stop_feed = budget_stop_feed; - dvbdemux1->write_to_decoder = NULL; - - dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - - dvb_dmx_init(&av7110->demux1); - - av7110->dmxdev1.filternum = 256; - av7110->dmxdev1.demux = &dvbdemux1->dmx; - av7110->dmxdev1.capabilities = 0; - - dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); - - dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); - printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); - } - return 0; -} - - -static void dvb_unregister(struct av7110 *av7110) -{ - struct dvb_demux *dvbdemux = &av7110->demux; - struct dvb_demux *dvbdemux1 = &av7110->demux1; - - dprintk(4, "%p\n", av7110); - - if (!av7110->registered) - return; - - if (budgetpatch) { - dvb_net_release(&av7110->dvb_net1); - dvbdemux->dmx.close(&dvbdemux1->dmx); - dvb_dmxdev_release(&av7110->dmxdev1); - dvb_dmx_release(&av7110->demux1); - } - - dvb_net_release(&av7110->dvb_net); - - dvbdemux->dmx.close(&dvbdemux->dmx); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); - dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); - - dvb_dmxdev_release(&av7110->dmxdev); - dvb_dmx_release(&av7110->demux); - - if (av7110->fe != NULL) { - dvb_unregister_frontend(av7110->fe); - dvb_frontend_detach(av7110->fe); - } - dvb_unregister_device(av7110->osd_dev); - av7110_av_unregister(av7110); - av7110_ca_unregister(av7110); -} - - -/**************************************************************************** - * I2C client commands - ****************************************************************************/ - -int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) -{ - u8 msg[2] = { reg, val }; - struct i2c_msg msgs; - - msgs.flags = 0; - msgs.addr = id / 2; - msgs.len = 2; - msgs.buf = msg; - return i2c_transfer(&av7110->i2c_adap, &msgs, 1); -} - -u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) -{ - u8 mm1[] = {0x00}; - u8 mm2[] = {0x00}; - struct i2c_msg msgs[2]; - - msgs[0].flags = 0; - msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = id / 2; - mm1[0] = reg; - msgs[0].len = 1; msgs[1].len = 1; - msgs[0].buf = mm1; msgs[1].buf = mm2; - i2c_transfer(&av7110->i2c_adap, msgs, 2); - - return mm2[0]; -} - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - - -static int check_firmware(struct av7110* av7110) -{ - u32 crc = 0, len = 0; - unsigned char *ptr; - - /* check for firmware magic */ - ptr = av7110->bin_fw; - if (ptr[0] != 'A' || ptr[1] != 'V' || - ptr[2] != 'F' || ptr[3] != 'W') { - printk("dvb-ttpci: this is not an av7110 firmware\n"); - return -EINVAL; - } - ptr += 4; - - /* check dpram file */ - crc = get_unaligned_be32(ptr); - ptr += 4; - len = get_unaligned_be32(ptr); - ptr += 4; - if (len >= 512) { - printk("dvb-ttpci: dpram file is way too big.\n"); - return -EINVAL; - } - if (crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of dpram file does not match.\n"); - return -EINVAL; - } - av7110->bin_dpram = ptr; - av7110->size_dpram = len; - ptr += len; - - /* check root file */ - crc = get_unaligned_be32(ptr); - ptr += 4; - len = get_unaligned_be32(ptr); - ptr += 4; - - if (len <= 200000 || len >= 300000 || - len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { - printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); - return -EINVAL; - } - if( crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of root file does not match.\n"); - return -EINVAL; - } - av7110->bin_root = ptr; - av7110->size_root = len; - return 0; -} - -static void put_firmware(struct av7110* av7110) -{ - vfree(av7110->bin_fw); -} - -static int get_firmware(struct av7110* av7110) -{ - int ret; - const struct firmware *fw; - - /* request the av7110 firmware, this will block until someone uploads it */ - ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); - if (ret) { - if (ret == -ENOENT) { - printk(KERN_ERR "dvb-ttpci: could not load firmware, file not found: dvb-ttpci-01.fw\n"); - printk(KERN_ERR "dvb-ttpci: usually this should be in /usr/lib/hotplug/firmware or /lib/firmware\n"); - printk(KERN_ERR "dvb-ttpci: and can be downloaded from https://linuxtv.org/download/dvb/firmware/\n"); - } else - printk(KERN_ERR "dvb-ttpci: cannot request firmware (error %i)\n", - ret); - return -EINVAL; - } - - if (fw->size <= 200000) { - printk("dvb-ttpci: this firmware is way too small.\n"); - release_firmware(fw); - return -EINVAL; - } - - /* check if the firmware is available */ - av7110->bin_fw = vmalloc(fw->size); - if (NULL == av7110->bin_fw) { - dprintk(1, "out of memory\n"); - release_firmware(fw); - return -ENOMEM; - } - - memcpy(av7110->bin_fw, fw->data, fw->size); - av7110->size_fw = fw->size; - if ((ret = check_firmware(av7110))) - vfree(av7110->bin_fw); - - release_firmware(fw); - return ret; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (p->frequency + 479500) / 125; - - if (p->frequency > 2000000) - pwr = 3; - else if (p->frequency > 1800000) - pwr = 2; - else if (p->frequency > 1600000) - pwr = 1; - else if (p->frequency > 1200000) - pwr = 0; - else if (p->frequency >= 1100000) - pwr = 1; - else - pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = { - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (p->frequency + 35937500 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85 | ((div >> 10) & 0x60); - data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1820_config alps_tdbe2_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - - - - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = p->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - - - -static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u32 f = p->frequency; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (f + 36125000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1820_config philips_cd1516_config = { - .demod_address = 0x09, - .xin = 57840000UL, - .invert = 1, - .selagc = VES1820_SELAGC_SIGNAMPERR, -}; - - - -static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div, pwr; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (p->frequency + 36200000) / 166666; - - if (p->frequency <= 782000000) - pwr = 1; - else - pwr = 2; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x85; - data[3] = pwr << 6; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) -{ -#if IS_ENABLED(CONFIG_DVB_SP8870) - struct av7110* av7110 = fe->dvb->priv; - - return request_firmware(fw, name, &av7110->dev->pci->dev); -#else - return -EINVAL; -#endif -} - -static const struct sp8870_config alps_tdlb7_config = { - - .demod_address = 0x71, - .request_firmware = alps_tdlb7_request_firmware, -}; - - -static u8 nexusca_stv0297_inittab[] = { - 0x80, 0x01, - 0x80, 0x00, - 0x81, 0x01, - 0x81, 0x00, - 0x00, 0x09, - 0x01, 0x69, - 0x03, 0x00, - 0x04, 0x00, - 0x07, 0x00, - 0x08, 0x00, - 0x20, 0x00, - 0x21, 0x40, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x40, - 0x25, 0x88, - 0x30, 0xff, - 0x31, 0x00, - 0x32, 0xff, - 0x33, 0x00, - 0x34, 0x50, - 0x35, 0x7f, - 0x36, 0x00, - 0x37, 0x20, - 0x38, 0x00, - 0x40, 0x1c, - 0x41, 0xff, - 0x42, 0x29, - 0x43, 0x00, - 0x44, 0xff, - 0x45, 0x00, - 0x46, 0x00, - 0x49, 0x04, - 0x4a, 0x00, - 0x4b, 0x7b, - 0x52, 0x30, - 0x55, 0xae, - 0x56, 0x47, - 0x57, 0xe1, - 0x58, 0x3a, - 0x5a, 0x1e, - 0x5b, 0x34, - 0x60, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x02, - 0x6b, 0x00, - 0x70, 0xff, - 0x71, 0x00, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x0c, - 0x80, 0x00, - 0x81, 0x00, - 0x82, 0x00, - 0x83, 0x00, - 0x84, 0x04, - 0x85, 0x80, - 0x86, 0x24, - 0x87, 0x78, - 0x88, 0x10, - 0x89, 0x00, - 0x90, 0x01, - 0x91, 0x01, - 0xa0, 0x04, - 0xa1, 0x00, - 0xa2, 0x00, - 0xb0, 0x91, - 0xb1, 0x0b, - 0xc0, 0x53, - 0xc1, 0x70, - 0xc2, 0x12, - 0xd0, 0x00, - 0xd1, 0x00, - 0xd2, 0x00, - 0xd3, 0x00, - 0xd4, 0x00, - 0xd5, 0x00, - 0xde, 0x00, - 0xdf, 0x00, - 0x61, 0x49, - 0x62, 0x0b, - 0x53, 0x08, - 0x59, 0x08, - 0xff, 0xff, -}; - -static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; - struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; - int i; - - div = (p->frequency + 36150000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xce; - - if (p->frequency < 45000000) - return -EINVAL; - else if (p->frequency < 137000000) - data[3] = 0x01; - else if (p->frequency < 403000000) - data[3] = 0x02; - else if (p->frequency < 860000000) - data[3] = 0x04; - else - return -EINVAL; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { - printk("nexusca: pll transfer failed!\n"); - return -EIO; - } - - // wait for PLL lock - for(i = 0; i < 20; i++) { - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) - if (data[0] & 0x40) break; - msleep(10); - } - - return 0; -} - -static struct stv0297_config nexusca_stv0297_config = { - - .demod_address = 0x1C, - .inittab = nexusca_stv0297_inittab, - .invert = 1, - .stop_during_read = 1, -}; - - - -static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; - u32 div; - u8 cfg, cpump, band_select; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = (36125000 + p->frequency) / 166666; - - cfg = 0x88; - - if (p->frequency < 175000000) - cpump = 2; - else if (p->frequency < 390000000) - cpump = 1; - else if (p->frequency < 470000000) - cpump = 2; - else if (p->frequency < 750000000) - cpump = 1; - else - cpump = 3; - - if (p->frequency < 175000000) - band_select = 0x0e; - else if (p->frequency < 470000000) - band_select = 0x05; - else - band_select = 0x03; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = ((div >> 10) & 0x60) | cfg; - data[3] = (cpump << 6) | band_select; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; - return 0; -} - -static struct l64781_config grundig_29504_401_config = { - .demod_address = 0x55, -}; - - - -static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) -{ - int ret = 0; - int synced = (status & FE_HAS_LOCK) ? 1 : 0; - - av7110->fe_status = status; - - if (av7110->fe_synced == synced) - return 0; - - if (av7110->playing) { - av7110->fe_synced = synced; - return 0; - } - - if (mutex_lock_interruptible(&av7110->pid_mutex)) - return -ERESTARTSYS; - - if (synced) { - ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], - av7110->pids[DMX_PES_TELETEXT], 0, - av7110->pids[DMX_PES_PCR]); - if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - } else { - ret = SetPIDs(av7110, 0, 0, 0, 0, 0); - if (!ret) { - ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); - if (!ret) - ret = av7110_wait_msgstate(av7110, GPMQBusy); - } - } - - if (!ret) - av7110->fe_synced = synced; - - mutex_unlock(&av7110->pid_mutex); - return ret; -} - -static int av7110_fe_set_frontend(struct dvb_frontend *fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_set_frontend(fe); - - return ret; -} - -static int av7110_fe_init(struct dvb_frontend* fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_init(fe); - return ret; -} - -static int av7110_fe_read_status(struct dvb_frontend *fe, - enum fe_status *status) -{ - struct av7110* av7110 = fe->dvb->priv; - - /* call the real implementation */ - int ret = av7110->fe_read_status(fe, status); - if (!ret) - if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) - ret = av7110_fe_lock_fix(av7110, *status); - return ret; -} - -static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_diseqc_reset_overload(fe); - return ret; -} - -static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_master_cmd = *cmd; - ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); - } - return ret; -} - -static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_minicmd = minicmd; - ret = av7110->fe_diseqc_send_burst(fe, minicmd); - } - return ret; -} - -static int av7110_fe_set_tone(struct dvb_frontend *fe, - enum fe_sec_tone_mode tone) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_tone = tone; - ret = av7110->fe_set_tone(fe, tone); - } - return ret; -} - -static int av7110_fe_set_voltage(struct dvb_frontend *fe, - enum fe_sec_voltage voltage) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) { - av7110->saved_voltage = voltage; - ret = av7110->fe_set_voltage(fe, voltage); - } - return ret; -} - -static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) -{ - struct av7110* av7110 = fe->dvb->priv; - - int ret = av7110_fe_lock_fix(av7110, 0); - if (!ret) - ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); - return ret; -} - -static void dvb_s_recover(struct av7110* av7110) -{ - av7110_fe_init(av7110->fe); - - av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); - if (av7110->saved_master_cmd.msg_len) { - msleep(20); - av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); - } - msleep(20); - av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); - msleep(20); - av7110_fe_set_tone(av7110->fe, av7110->saved_tone); - - av7110_fe_set_frontend(av7110->fe); -} - -static u8 read_pwm(struct av7110* av7110) -{ - u8 b = 0xff; - u8 pwm; - struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, - { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; - - if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; - - return pwm; -} - -static int frontend_init(struct av7110 *av7110) -{ - int ret; - - if (av7110->dev->pci->subsystem_vendor == 0x110a) { - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) - av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, - &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } - break; - } - - } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X - case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X - case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE - - // try the ALPS BSRV2 first of all - av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - // try the ALPS BSRU6 now - av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - av7110->fe->tuner_priv = &av7110->i2c_adap; - - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - // Try the grundig 29504-451 - av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - break; - } - - /* Try DVB-C cards */ - switch(av7110->dev->pci->subsystem_device) { - case 0x0000: - /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ - av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, - read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } - break; - case 0x0003: - /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ - av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, - read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } - break; - } - break; - - case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X - { - struct dvb_frontend *fe; - - // try ALPS TDLB7 first, then Grundig 29504-401 - fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); - if (fe) { - fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; - av7110->fe = fe; - break; - } - } - fallthrough; - - case 0x0008: // Hauppauge/TT DVB-T - // Grundig 29504-401 - av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); - if (av7110->fe) - av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; - break; - - case 0x0002: // Hauppauge/TT DVB-C premium rev2.X - - av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } - break; - - case 0x0004: // Galaxis DVB-S rev1.3 - /* ALPS BSRV2 */ - av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - } - break; - - case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ - /* Grundig 29504-451 */ - av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; - av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; - av7110->fe->ops.set_tone = av7110_set_tone; - av7110->recover = dvb_s_recover; - } - break; - - case 0x000A: // Hauppauge/TT Nexus-CA rev1.X - - av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; - - /* set TDA9819 into DVB mode */ - saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - - /* tuner on this needs a slower i2c bus speed */ - av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - break; - } - break; - - case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ - /* ALPS BSBE1 */ - av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; - av7110->fe->tuner_priv = &av7110->i2c_adap; - - if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { - printk("dvb-ttpci: LNBP21 not found!\n"); - if (av7110->fe->ops.release) - av7110->fe->ops.release(av7110->fe); - av7110->fe = NULL; - } else { - av7110->fe->ops.dishnetwork_send_legacy_command = NULL; - av7110->recover = dvb_s_recover; - } - } - break; - } - } - - if (!av7110->fe) { - /* FIXME: propagate the failure code from the lower layers */ - ret = -ENOMEM; - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - av7110->dev->pci->vendor, - av7110->dev->pci->device, - av7110->dev->pci->subsystem_vendor, - av7110->dev->pci->subsystem_device); - } else { - FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); - FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); - FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); - FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); - FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); - - ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); - if (ret < 0) { - printk("av7110: Frontend registration failed!\n"); - dvb_frontend_detach(av7110->fe); - av7110->fe = NULL; - } - } - return ret; -} - -/* Budgetpatch note: - * Original hardware design by Roberto Deza: - * There is a DVB_Wiki at - * https://linuxtv.org - * - * New software triggering design by Emard that works on - * original Roberto Deza's hardware: - * - * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. - * GPIO3 is in budget-patch hardware connectd to port B VSYNC - * HS is an internal event of 7146, accessible with RPS - * and temporarily raised high every n lines - * (n in defined in the RPS_THRESH1 counter threshold) - * I think HS is raised high on the beginning of the n-th line - * and remains high until this n-th line that triggered - * it is completely received. When the reception of n-th line - * ends, HS is lowered. - * - * To transmit data over DMA, 7146 needs changing state at - * port B VSYNC pin. Any changing of port B VSYNC will - * cause some DMA data transfer, with more or less packets loss. - * It depends on the phase and frequency of VSYNC and - * the way of 7146 is instructed to trigger on port B (defined - * in DD1_INIT register, 3rd nibble from the right valid - * numbers are 0-7, see datasheet) - * - * The correct triggering can minimize packet loss, - * dvbtraffic should give this stable bandwidths: - * 22k transponder = 33814 kbit/s - * 27.5k transponder = 38045 kbit/s - * by experiment it is found that the best results - * (stable bandwidths and almost no packet loss) - * are obtained using DD1_INIT triggering number 2 - * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) - * and a VSYNC phase that occurs in the middle of DMA transfer - * (about byte 188*512=96256 in the DMA window). - * - * Phase of HS is still not clear to me how to control, - * It just happens to be so. It can be seen if one enables - * RPS_IRQ and print Event Counter 1 in vpeirq(). Every - * time RPS_INTERRUPT is called, the Event Counter 1 will - * increment. That's how the 7146 is programmed to do event - * counting in this budget-patch.c - * I *think* HPS setting has something to do with the phase - * of HS but I can't be 100% sure in that. - * - * hardware debug note: a working budget card (including budget patch) - * with vpeirq() interrupt setup in mode "0x90" (every 64K) will - * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes - * and that means 3*25=75 Hz of interrupt frequency, as seen by - * watch cat /proc/interrupts - * - * If this frequency is 3x lower (and data received in the DMA - * buffer don't start with 0x47, but in the middle of packets, - * whose lengths appear to be like 188 292 188 104 etc. - * this means VSYNC line is not connected in the hardware. - * (check soldering pcb and pins) - * The same behaviour of missing VSYNC can be duplicated on budget - * cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. - */ -static int av7110_attach(struct saa7146_dev* dev, - struct saa7146_pci_extension_data *pci_ext) -{ - const int length = TS_WIDTH * TS_HEIGHT; - struct pci_dev *pdev = dev->pci; - struct av7110 *av7110; - struct task_struct *thread; - int ret, count = 0; - - dprintk(4, "dev: %p\n", dev); - - /* Set RPS_IRQ to 1 to track rps1 activity. - * Enabling this won't send any interrupt to PC CPU. - */ -#define RPS_IRQ 0 - - if (budgetpatch == 1) { - budgetpatch = 0; - /* autodetect the presence of budget patch - * this only works if saa7146 has been recently - * reset with MASK_31 to MC1 - * - * will wait for VBI_B event (vertical blank at port B) - * and will reset GPIO3 after VBI_B is detected. - * (GPIO3 should be raised high by CPU to - * test if GPIO3 will generate vertical blank signal - * in budget patch GPIO3 is connected to VSYNC_B - */ - - /* RESET SAA7146 */ - saa7146_write(dev, MC1, MASK_31); - /* autodetection success seems to be time-dependend after reset */ - - /* Fix VSYNC level */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - /* set vsync_b triggering */ - saa7146_write(dev, DD1_STREAM_B, 0); - /* port B VSYNC at rising edge */ - saa7146_write(dev, DD1_INIT, 0x00000200); - saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI - saa7146_write(dev, MC2, - 1 * (MASK_08 | MASK_24) | // BRS control - 0 * (MASK_09 | MASK_25) | // a - 1 * (MASK_10 | MASK_26) | // b - 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 - 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 - 0 * (MASK_01 | MASK_15) // DEBI - ); - - /* start writing RPS1 code from beginning */ - count = 0; - /* Disable RPS1 */ - saa7146_write(dev, MC1, MASK_29); - /* RPS1 timeout disable */ - saa7146_write(dev, RPS_TOV1, 0); - WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - /* issue RPS1 interrupt to increment counter */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - WRITE_RPS1(CMD_STOP); - /* Jump to begin of RPS program as safety measure (p37) */ - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - -#if RPS_IRQ - /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - /* Set RPS1 Address register to point to RPS code (r108 p42) */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - /* Enable RPS1, (rFC p33) */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); - - mdelay(10); - /* now send VSYNC_B to rps1 by rising GPIO3 */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - mdelay(10); - /* if rps1 responded by lowering the GPIO3, - * then we have budgetpatch hardware - */ - if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { - budgetpatch = 1; - printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); - } - /* Disable RPS1 */ - saa7146_write(dev, MC1, ( MASK_29 )); -#if RPS_IRQ - printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); -#endif - } - - /* prepare the av7110 device struct */ - av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); - if (!av7110) { - dprintk(1, "out of memory\n"); - return -ENOMEM; - } - - av7110->card_name = (char*) pci_ext->ext_priv; - av7110->dev = dev; - dev->ext_priv = av7110; - - ret = get_firmware(av7110); - if (ret < 0) - goto err_kfree_0; - - ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, - THIS_MODULE, &dev->pci->dev, adapter_nr); - if (ret < 0) - goto err_put_firmware_1; - - /* the Siemens DVB needs this if you want to have the i2c chips - get recognized before the main driver is fully loaded */ - saa7146_write(dev, GPIO_CTRL, 0x500000); - - strscpy(av7110->i2c_adap.name, pci_ext->ext_priv, - sizeof(av7110->i2c_adap.name)); - - saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ - - ret = i2c_add_adapter(&av7110->i2c_adap); - if (ret < 0) - goto err_dvb_unregister_adapter_2; - - ttpci_eeprom_parse_mac(&av7110->i2c_adap, - av7110->dvb_adapter.proposed_mac); - ret = -ENOMEM; - - /* full-ts mod? */ - if (full_ts) - av7110->full_ts = true; - - /* check for full-ts flag in eeprom */ - if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { - u8 flags = i2c_readreg(av7110, 0xaa, 2); - if (flags != 0xff && (flags & 0x01)) - av7110->full_ts = true; - } - - if (av7110->full_ts) { - printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); - spin_lock_init(&av7110->feedlock1); - av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, - &av7110->pt); - if (!av7110->grabbing) - goto err_i2c_del_3; - - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, MC2, (MASK_10 | MASK_26)); - - saa7146_write(dev, DD1_INIT, 0x00000600); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - saa7146_write(dev, BRS_CTRL, 0x60000000); - saa7146_write(dev, MC2, MASK_08 | MASK_24); - - /* dma3 */ - saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - saa7146_write(dev, MC2, MASK_04 | MASK_20); - - tasklet_setup(&av7110->vpe_tasklet, vpeirq); - - } else if (budgetpatch) { - spin_lock_init(&av7110->feedlock1); - av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, - &av7110->pt); - if (!av7110->grabbing) - goto err_i2c_del_3; - - saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); - saa7146_write(dev, BCS_CTRL, 0x80400040); - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, DD1_INIT, 0x03000200); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - saa7146_write(dev, BRS_CTRL, 0x60000000); - saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); - saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); - - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - - /* upload all */ - saa7146_write(dev, MC2, 0x077c077c); - saa7146_write(dev, GPIO_CTRL, 0x000000); -#if RPS_IRQ - /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ - count = 0; - - /* Wait Source Line Counter Threshold (p36) */ - WRITE_RPS1(CMD_PAUSE | EVT_HS); - /* Set GPIO3=1 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); -#if RPS_IRQ - /* issue RPS1 interrupt */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - /* Wait reset Source Line Counter Threshold (p36) */ - WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); - /* Set GPIO3=0 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - /* issue RPS1 interrupt */ - WRITE_RPS1(CMD_INTERRUPT); -#endif - /* Jump to begin of RPS program (p37) */ - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - - /* Fix VSYNC level */ - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - /* Set RPS1 Address register to point to RPS code (r108 p42) */ - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - /* Set Source Line Counter Threshold, using BRS (rCC p43) - * It generates HS event every TS_HEIGHT lines - * this is related to TS_WIDTH set in register - * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits - * are set to TS_WIDTH bytes (TS_WIDTH=2*188), - * then RPS_THRESH1 should be set to trigger - * every TS_HEIGHT (512) lines. - */ - saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); - - /* Enable RPS1 (rFC p33) */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - /* end of budgetpatch register initialization */ - tasklet_setup(&av7110->vpe_tasklet, vpeirq); - } else { - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - saa7146_write(dev, BCS_CTRL, 0x80400040); - - /* set dd1 stream a & b */ - saa7146_write(dev, DD1_STREAM_B, 0x00000000); - saa7146_write(dev, DD1_INIT, 0x03000000); - saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - /* upload all */ - saa7146_write(dev, MC2, 0x077c077c); - saa7146_write(dev, GPIO_CTRL, 0x000000); - } - - tasklet_setup(&av7110->debi_tasklet, debiirq); - tasklet_setup(&av7110->gpio_tasklet, gpioirq); - - mutex_init(&av7110->pid_mutex); - - /* locks for data transfers from/to AV7110 */ - spin_lock_init(&av7110->debilock); - mutex_init(&av7110->dcomlock); - av7110->debitype = -1; - - /* default OSD window */ - av7110->osdwin = 1; - mutex_init(&av7110->osd_mutex); - - /* TV standard */ - av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC - : AV7110_VIDEO_MODE_PAL; - - /* ARM "watchdog" */ - init_waitqueue_head(&av7110->arm_wait); - av7110->arm_thread = NULL; - - /* allocate and init buffers */ - av7110->debi_virt = dma_alloc_coherent(&pdev->dev, 8192, - &av7110->debi_bus, GFP_KERNEL); - if (!av7110->debi_virt) - goto err_saa71466_vfree_4; - - - av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); - if (!av7110->iobuf) - goto err_pci_free_5; - - ret = av7110_av_init(av7110); - if (ret < 0) - goto err_iobuf_vfree_6; - - /* init BMP buffer */ - av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; - init_waitqueue_head(&av7110->bmpq); - - ret = av7110_ca_init(av7110); - if (ret < 0) - goto err_av7110_av_exit_7; - - /* load firmware into AV7110 cards */ - ret = av7110_bootarm(av7110); - if (ret < 0) - goto err_av7110_ca_exit_8; - - ret = av7110_firmversion(av7110); - if (ret < 0) - goto err_stop_arm_9; - - if (FW_VERSION(av7110->arm_app)<0x2501) - printk(KERN_WARNING - "dvb-ttpci: Warning, firmware version 0x%04x is too old. System might be unstable!\n", - FW_VERSION(av7110->arm_app)); - - thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); - if (IS_ERR(thread)) { - ret = PTR_ERR(thread); - goto err_stop_arm_9; - } - av7110->arm_thread = thread; - - /* set initial volume in mixer struct */ - av7110->mixer.volume_left = volume; - av7110->mixer.volume_right = volume; - - ret = av7110_register(av7110); - if (ret < 0) - goto err_arm_thread_stop_10; - - init_av7110_av(av7110); - - /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ - ret = av7110_init_v4l(av7110); - if (ret < 0) - goto err_av7110_unregister_11; - - av7110->dvb_adapter.priv = av7110; - ret = frontend_init(av7110); - if (ret < 0) - goto err_av7110_exit_v4l_12; - - mutex_init(&av7110->ioctl_mutex); - -#if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_ir_init(av7110); -#endif - printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); - av7110_num++; -out: - return ret; - -err_av7110_exit_v4l_12: - av7110_exit_v4l(av7110); -err_av7110_unregister_11: - dvb_unregister(av7110); -err_arm_thread_stop_10: - av7110_arm_sync(av7110); -err_stop_arm_9: - /* Nothing to do. Rejoice. */ -err_av7110_ca_exit_8: - av7110_ca_exit(av7110); -err_av7110_av_exit_7: - av7110_av_exit(av7110); -err_iobuf_vfree_6: - vfree(av7110->iobuf); -err_pci_free_5: - dma_free_coherent(&pdev->dev, 8192, av7110->debi_virt, - av7110->debi_bus); -err_saa71466_vfree_4: - if (av7110->grabbing) - saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); -err_i2c_del_3: - i2c_del_adapter(&av7110->i2c_adap); -err_dvb_unregister_adapter_2: - dvb_unregister_adapter(&av7110->dvb_adapter); -err_put_firmware_1: - put_firmware(av7110); -err_kfree_0: - kfree(av7110); - goto out; -} - -static int av7110_detach(struct saa7146_dev* saa) -{ - struct av7110 *av7110 = saa->ext_priv; - dprintk(4, "%p\n", av7110); - -#if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_ir_exit(av7110); -#endif - if (budgetpatch || av7110->full_ts) { - if (budgetpatch) { - /* Disable RPS1 */ - saa7146_write(saa, MC1, MASK_29); - /* VSYNC LOW (inactive) */ - saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); - } - saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ - SAA7146_IER_DISABLE(saa, MASK_10); - SAA7146_ISR_CLEAR(saa, MASK_10); - msleep(50); - tasklet_kill(&av7110->vpe_tasklet); - saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); - } - av7110_exit_v4l(av7110); - - av7110_arm_sync(av7110); - - tasklet_kill(&av7110->debi_tasklet); - tasklet_kill(&av7110->gpio_tasklet); - - dvb_unregister(av7110); - - SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); - - av7110_ca_exit(av7110); - av7110_av_exit(av7110); - - vfree(av7110->iobuf); - dma_free_coherent(&saa->pci->dev, 8192, av7110->debi_virt, - av7110->debi_bus); - - i2c_del_adapter(&av7110->i2c_adap); - - dvb_unregister_adapter (&av7110->dvb_adapter); - - av7110_num--; - - put_firmware(av7110); - - kfree(av7110); - - saa->ext_priv = NULL; - - return 0; -} - - -static void av7110_irq(struct saa7146_dev* dev, u32 *isr) -{ - struct av7110 *av7110 = dev->ext_priv; - - //print_time("av7110_irq"); - - /* Note: Don't try to handle the DEBI error irq (MASK_18), in - * intel mode the timeout is asserted all the time... - */ - - if (*isr & MASK_19) { - //printk("av7110_irq: DEBI\n"); - /* Note 1: The DEBI irq is level triggered: We must enable it - * only after we started a DMA xfer, and disable it here - * immediately, or it will be signalled all the time while - * DEBI is idle. - * Note 2: You would think that an irq which is masked is - * not signalled by the hardware. Not so for the SAA7146: - * An irq is signalled as long as the corresponding bit - * in the ISR is set, and disabling irqs just prevents the - * hardware from setting the ISR bit. This means a) that we - * must clear the ISR *after* disabling the irq (which is why - * we must do it here even though saa7146_core did it already), - * and b) that if we were to disable an edge triggered irq - * (like the gpio irqs sadly are) temporarily we would likely - * loose some. This sucks :-( - */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19); - tasklet_schedule(&av7110->debi_tasklet); - } - - if (*isr & MASK_03) { - //printk("av7110_irq: GPIO\n"); - tasklet_schedule(&av7110->gpio_tasklet); - } - - if (*isr & MASK_10) - tasklet_schedule(&av7110->vpe_tasklet); -} - - -static struct saa7146_extension av7110_extension_driver; - -#define MAKE_AV7110_INFO(x_var,x_name) \ -static struct saa7146_pci_extension_data x_var = { \ - .ext_priv = x_name, \ - .ext = &av7110_extension_driver } - -MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); -MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); -MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); -MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); -MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); -MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); -MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); -MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); -MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); -MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); -MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), - MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), - MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), - MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), - MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), - MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), - MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), - MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), - MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), - MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), - MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), - -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? - - { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - - -static struct saa7146_extension av7110_extension_driver = { - .name = "av7110", - .flags = SAA7146_USE_I2C_IRQ, - - .module = THIS_MODULE, - .pci_tbl = &pci_tbl[0], - .attach = av7110_attach, - .detach = av7110_detach, - - .irq_mask = MASK_19 | MASK_03 | MASK_10, - .irq_func = av7110_irq, -}; - - -static int __init av7110_init(void) -{ - return saa7146_register_extension(&av7110_extension_driver); -} - - -static void __exit av7110_exit(void) -{ - saa7146_unregister_extension(&av7110_extension_driver); -} - -module_init(av7110_init); -module_exit(av7110_exit); - -MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by Siemens, Technotrend, Hauppauge"); -MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h deleted file mode 100644 index 9fde69b38f1c..000000000000 --- a/drivers/staging/media/av7110/av7110.h +++ /dev/null @@ -1,315 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _AV7110_H_ -#define _AV7110_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "dvb_filter.h" -#include -#include -#include -#include "ves1820.h" -#include "ves1x93.h" -#include "stv0299.h" -#include "tda8083.h" -#include "sp8870.h" -#include "stv0297.h" -#include "l64781.h" - -#include "saa7146_vv.h" - - -#define ANALOG_TUNER_VES1820 1 -#define ANALOG_TUNER_STV0297 2 - -extern int av7110_debug; - -#define dprintk(level, fmt, arg...) do { \ - if (level & av7110_debug) \ - printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ - __func__, ##arg); \ -} while (0) - -#define MAXFILT 32 - -enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; - -enum av7110_video_mode { - AV7110_VIDEO_MODE_PAL = 0, - AV7110_VIDEO_MODE_NTSC = 1 -}; - -struct av7110_p2t { - u8 pes[TS_SIZE]; - u8 counter; - long int pos; - int frags; - struct dvb_demux_feed *feed; -}; - -/* video MPEG decoder events: */ -/* (code copied from dvb_frontend.c, should maybe be factored out...) */ -#define MAX_VIDEO_EVENT 8 -struct dvb_video_events { - struct video_event events[MAX_VIDEO_EVENT]; - int eventw; - int eventr; - int overflow; - wait_queue_head_t wait_queue; - spinlock_t lock; -}; - - -struct av7110; - -/* infrared remote control */ -struct infrared { - struct rc_dev *rcdev; - char input_phys[32]; - u32 ir_config; -}; - -/* place to store all the necessary device information */ -struct av7110 { - - /* devices */ - - struct dvb_device dvb_dev; - struct dvb_net dvb_net; - - struct video_device v4l_dev; - struct video_device vbi_dev; - - struct saa7146_dev *dev; - - struct i2c_adapter i2c_adap; - - char *card_name; - - /* support for analog module of dvb-c */ - int analog_tuner_flags; - int current_input; - u32 current_freq; - - struct tasklet_struct debi_tasklet; - struct tasklet_struct gpio_tasklet; - - int adac_type; /* audio DAC type */ -#define DVB_ADAC_TI 0 -#define DVB_ADAC_CRYSTAL 1 -#define DVB_ADAC_MSP34x0 2 -#define DVB_ADAC_MSP34x5 3 -#define DVB_ADAC_NONE -1 - - - /* buffers */ - - void *iobuf; /* memory for all buffers */ - struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ -#define AVOUTLEN (128*1024) - struct dvb_ringbuffer aout; /* buffer for audio */ -#define AOUTLEN (64*1024) - void *bmpbuf; -#define BMPLEN (8*32768+1024) - - /* bitmap buffers and states */ - - int bmpp; - int bmplen; - volatile int bmp_state; -#define BMP_NONE 0 -#define BMP_LOADING 1 -#define BMP_LOADED 2 - wait_queue_head_t bmpq; - - - /* DEBI and polled command interface */ - - spinlock_t debilock; - struct mutex dcomlock; - volatile int debitype; - volatile int debilen; - - - /* Recording and playback flags */ - - int rec_mode; - int playing; -#define RP_NONE 0 -#define RP_VIDEO 1 -#define RP_AUDIO 2 -#define RP_AV 3 - - - /* OSD */ - - int osdwin; /* currently active window */ - u16 osdbpp[8]; - struct mutex osd_mutex; - - /* CA */ - - struct ca_slot_info ci_slot[2]; - - enum av7110_video_mode vidmode; - struct dmxdev dmxdev; - struct dvb_demux demux; - - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - - /* for budget mode demux1 */ - struct dmxdev dmxdev1; - struct dvb_demux demux1; - struct dvb_net dvb_net1; - spinlock_t feedlock1; - int feeding1; - u32 ttbp; - unsigned char *grabbing; - struct saa7146_pgtable pt; - struct tasklet_struct vpe_tasklet; - bool full_ts; - - int fe_synced; - struct mutex pid_mutex; - - int video_blank; - struct video_status videostate; - u16 display_panscan; - int display_ar; - int trickmode; -#define TRICK_NONE 0 -#define TRICK_FAST 1 -#define TRICK_SLOW 2 -#define TRICK_FREEZE 3 - struct audio_status audiostate; - - struct dvb_demux_filter *handle2filter[32]; - struct av7110_p2t p2t_filter[MAXFILT]; - struct dvb_filter_pes2ts p2t[2]; - struct ipack ipack[2]; - u8 *kbuf[2]; - - int sinfo; - int feeding; - - int arm_errors; - int registered; - - - /* AV711X */ - - u32 arm_fw; - u32 arm_rtsl; - u32 arm_vid; - u32 arm_app; - u32 avtype; - int arm_ready; - struct task_struct *arm_thread; - wait_queue_head_t arm_wait; - u16 arm_loops; - - void *debi_virt; - dma_addr_t debi_bus; - - u16 pids[DMX_PES_OTHER]; - - struct dvb_ringbuffer ci_rbuffer; - struct dvb_ringbuffer ci_wbuffer; - - struct audio_mixer mixer; - - struct dvb_adapter dvb_adapter; - struct dvb_device *video_dev; - struct dvb_device *audio_dev; - struct dvb_device *ca_dev; - struct dvb_device *osd_dev; - - struct dvb_video_events video_events; - video_size_t video_size; - - u16 wssMode; - u16 wssData; - - struct infrared ir; - - /* firmware stuff */ - unsigned char *bin_fw; - unsigned long size_fw; - - unsigned char *bin_dpram; - unsigned long size_dpram; - - unsigned char *bin_root; - unsigned long size_root; - - struct dvb_frontend* fe; - enum fe_status fe_status; - - struct mutex ioctl_mutex; - - /* crash recovery */ - void (*recover)(struct av7110* av7110); - enum fe_sec_voltage saved_voltage; - enum fe_sec_tone_mode saved_tone; - struct dvb_diseqc_master_cmd saved_master_cmd; - enum fe_sec_mini_cmd saved_minicmd; - - int (*fe_init)(struct dvb_frontend* fe); - int (*fe_read_status)(struct dvb_frontend *fe, enum fe_status *status); - int (*fe_diseqc_reset_overload)(struct dvb_frontend *fe); - int (*fe_diseqc_send_master_cmd)(struct dvb_frontend *fe, - struct dvb_diseqc_master_cmd *cmd); - int (*fe_diseqc_send_burst)(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd); - int (*fe_set_tone)(struct dvb_frontend *fe, - enum fe_sec_tone_mode tone); - int (*fe_set_voltage)(struct dvb_frontend *fe, - enum fe_sec_voltage voltage); - int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend *fe, - unsigned long cmd); - int (*fe_set_frontend)(struct dvb_frontend *fe); -}; - - -extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid); - -void av7110_ir_handler(struct av7110 *av7110, u32 ircom); -int av7110_set_ir_config(struct av7110 *av7110); -int av7110_ir_init(struct av7110 *av7110); -void av7110_ir_exit(struct av7110 *av7110); - -/* msp3400 i2c subaddresses */ -#define MSP_WR_DEM 0x10 -#define MSP_RD_DEM 0x11 -#define MSP_WR_DSP 0x12 -#define MSP_RD_DSP 0x13 - -extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); -extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); -extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); - - -extern int av7110_init_analog_module(struct av7110 *av7110); -extern int av7110_init_v4l(struct av7110 *av7110); -extern int av7110_exit_v4l(struct av7110 *av7110); - -#endif /* _AV7110_H_ */ diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c deleted file mode 100644 index 0bf513c26b6b..000000000000 --- a/drivers/staging/media/av7110/av7110_av.c +++ /dev/null @@ -1,1681 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * av7110_av.c: audio and video MPEG decoder stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * the project's page is at https://linuxtv.org - */ - -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" -#include "av7110_ipack.h" - -/* MPEG-2 (ISO 13818 / H.222.0) stream types */ -#define PROG_STREAM_MAP 0xBC -#define PRIVATE_STREAM1 0xBD -#define PADDING_STREAM 0xBE -#define PRIVATE_STREAM2 0xBF -#define AUDIO_STREAM_S 0xC0 -#define AUDIO_STREAM_E 0xDF -#define VIDEO_STREAM_S 0xE0 -#define VIDEO_STREAM_E 0xEF -#define ECM_STREAM 0xF0 -#define EMM_STREAM 0xF1 -#define DSM_CC_STREAM 0xF2 -#define ISO13522_STREAM 0xF3 -#define PROG_STREAM_DIR 0xFF - -#define PTS_DTS_FLAGS 0xC0 - -//pts_dts flags -#define PTS_ONLY 0x80 -#define PTS_DTS 0xC0 -#define TS_SIZE 188 -#define TRANS_ERROR 0x80 -#define PAY_START 0x40 -#define TRANS_PRIO 0x20 -#define PID_MASK_HI 0x1F -//flags -#define TRANS_SCRMBL1 0x80 -#define TRANS_SCRMBL2 0x40 -#define ADAPT_FIELD 0x20 -#define PAYLOAD 0x10 -#define COUNT_MASK 0x0F - -// adaptation flags -#define DISCON_IND 0x80 -#define RAND_ACC_IND 0x40 -#define ES_PRI_IND 0x20 -#define PCR_FLAG 0x10 -#define OPCR_FLAG 0x08 -#define SPLICE_FLAG 0x04 -#define TRANS_PRIV 0x02 -#define ADAP_EXT_FLAG 0x01 - -// adaptation extension flags -#define LTW_FLAG 0x80 -#define PIECE_RATE 0x40 -#define SEAM_SPLICE 0x20 - - -static void p_to_t(u8 const *buf, long int length, u16 pid, - u8 *counter, struct dvb_demux_feed *feed); -static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); - - -int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; - - if (!(dvbdmxfeed->ts_type & TS_PACKET)) - return 0; - if (buf[3] == 0xe0) // video PES do not have a length in TS - buf[4] = buf[5] = 0; - if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) - return dvbdmxfeed->cb.ts(buf, len, NULL, 0, - &dvbdmxfeed->feed.ts, NULL); - else - return dvb_filter_pes2ts(p2t, buf, len, 1); -} - -static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; - - dvbdmxfeed->cb.ts(data, 188, NULL, 0, - &dvbdmxfeed->feed.ts, NULL); - return 0; -} - -int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed) -{ - int ret = 0; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - - dprintk(2, "av7110:%p, dvb_demux_feed:%p\n", av7110, dvbdmxfeed); - - if (av7110->playing || (av7110->rec_mode & av)) - return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - dvbdmx->recording = 1; - av7110->rec_mode |= av; - - switch (av7110->rec_mode) { - case RP_AUDIO: - dvb_filter_pes2ts_init(&av7110->p2t[0], - dvbdmx->pesfilter[0]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); - break; - - case RP_VIDEO: - dvb_filter_pes2ts_init(&av7110->p2t[1], - dvbdmx->pesfilter[1]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); - break; - - case RP_AV: - dvb_filter_pes2ts_init(&av7110->p2t[0], - dvbdmx->pesfilter[0]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); - dvb_filter_pes2ts_init(&av7110->p2t[1], - dvbdmx->pesfilter[1]->pid, - dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); - break; - } - return ret; -} - -int av7110_av_start_play(struct av7110 *av7110, int av) -{ - int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->rec_mode) - return -EBUSY; - if (av7110->playing & av) - return -EBUSY; - - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - - if (av7110->playing == RP_NONE) { - av7110_ipack_reset(&av7110->ipack[0]); - av7110_ipack_reset(&av7110->ipack[1]); - } - - av7110->playing |= av; - switch (av7110->playing) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); - av7110->sinfo = 0; - break; - case RP_AV: - av7110->sinfo = 0; - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); - break; - } - return ret; -} - -int av7110_av_stop(struct av7110 *av7110, int av) -{ - int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); - - if (!(av7110->playing & av) && !(av7110->rec_mode & av)) - return 0; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - if (av7110->playing) { - av7110->playing &= ~av; - switch (av7110->playing) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); - break; - case RP_NONE: - ret = av7110_set_vidmode(av7110, av7110->vidmode); - break; - } - } else { - av7110->rec_mode &= ~av; - switch (av7110->rec_mode) { - case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); - break; - case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); - break; - case RP_NONE: - break; - } - } - return ret; -} - - -int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) -{ - int len; - u32 sync; - u16 blen; - - if (!dlen) { - wake_up(&buf->queue); - return -1; - } - while (1) { - len = dvb_ringbuffer_avail(buf); - if (len < 6) { - wake_up(&buf->queue); - return -1; - } - sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; - sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; - sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; - sync |= DVB_RINGBUFFER_PEEK(buf, 3); - - if (((sync &~ 0x0f) == 0x000001e0) || - ((sync &~ 0x1f) == 0x000001c0) || - (sync == 0x000001bd)) - break; - printk("resync\n"); - DVB_RINGBUFFER_SKIP(buf, 1); - } - blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; - blen |= DVB_RINGBUFFER_PEEK(buf, 5); - blen += 6; - if (len < blen || blen > dlen) { - //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); - wake_up(&buf->queue); - return -1; - } - - dvb_ringbuffer_read(buf, dest, (size_t) blen); - - dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", - (unsigned long) buf->pread, (unsigned long) buf->pwrite); - wake_up(&buf->queue); - return blen; -} - - -int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, - unsigned int volright) -{ - unsigned int vol, val, balance = 0; - int err; - - dprintk(2, "av7110:%p, \n", av7110); - - av7110->mixer.volume_left = volleft; - av7110->mixer.volume_right = volright; - - switch (av7110->adac_type) { - case DVB_ADAC_TI: - volleft = (volleft * 256) / 1036; - volright = (volright * 256) / 1036; - if (volleft > 0x3f) - volleft = 0x3f; - if (volright > 0x3f) - volright = 0x3f; - if ((err = SendDAC(av7110, 3, 0x80 + volleft))) - return err; - return SendDAC(av7110, 4, volright); - - case DVB_ADAC_CRYSTAL: - volleft = 127 - volleft / 2; - volright = 127 - volright / 2; - i2c_writereg(av7110, 0x20, 0x03, volleft); - i2c_writereg(av7110, 0x20, 0x04, volright); - return 0; - - case DVB_ADAC_MSP34x0: - vol = (volleft > volright) ? volleft : volright; - val = (vol * 0x73 / 255) << 8; - if (vol > 0) - balance = ((volright - volleft) * 127) / vol; - msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ - msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ - return 0; - - case DVB_ADAC_MSP34x5: - vol = (volleft > volright) ? volleft : volright; - val = (vol * 0x73 / 255) << 8; - if (vol > 0) - balance = ((volright - volleft) * 127) / vol; - msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ - return 0; - } - - return 0; -} - -int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) -{ - int ret; - dprintk(2, "av7110:%p, \n", av7110); - - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); - - if (!ret && !av7110->playing) { - ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], - av7110->pids[DMX_PES_TELETEXT], - 0, av7110->pids[DMX_PES_PCR]); - if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); - } - return ret; -} - - -static enum av7110_video_mode sw2mode[16] = { - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, - AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, -}; - -static int get_video_format(struct av7110 *av7110, u8 *buf, int count) -{ - int i; - int hsize, vsize; - int sw; - u8 *p; - int ret = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->sinfo) - return 0; - for (i = 7; i < count - 10; i++) { - p = buf + i; - if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) - continue; - p += 4; - hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); - vsize = ((p[1] &0x0F) << 8) | (p[2]); - sw = (p[3] & 0x0F); - ret = av7110_set_vidmode(av7110, sw2mode[sw]); - if (!ret) { - dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); - av7110->sinfo = 1; - } - break; - } - return ret; -} - - -/**************************************************************************** - * I/O buffer management and control - ****************************************************************************/ - -static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, - const u8 *buf, unsigned long count) -{ - unsigned long todo = count; - int free; - - while (todo > 0) { - if (dvb_ringbuffer_free(rbuf) < 2048) { - if (wait_event_interruptible(rbuf->queue, - (dvb_ringbuffer_free(rbuf) >= 2048))) - return count - todo; - } - free = dvb_ringbuffer_free(rbuf); - if (free > todo) - free = todo; - dvb_ringbuffer_write(rbuf, buf, free); - todo -= free; - buf += free; - } - - return count - todo; -} - -static void play_video_cb(u8 *buf, int count, void *priv) -{ - struct av7110 *av7110 = (struct av7110 *) priv; - dprintk(2, "av7110:%p, \n", av7110); - - if ((buf[3] & 0xe0) == 0xe0) { - get_video_format(av7110, buf, count); - aux_ring_buffer_write(&av7110->avout, buf, count); - } else - aux_ring_buffer_write(&av7110->aout, buf, count); -} - -static void play_audio_cb(u8 *buf, int count, void *priv) -{ - struct av7110 *av7110 = (struct av7110 *) priv; - dprintk(2, "av7110:%p, \n", av7110); - - aux_ring_buffer_write(&av7110->aout, buf, count); -} - - -#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) - -static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - struct dvb_ringbuffer *rb; - u8 *kb; - unsigned long todo = count; - - dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); - - rb = (type) ? &av7110->avout : &av7110->aout; - kb = av7110->kbuf[type]; - - if (!kb) - return -ENOBUFS; - - if (nonblock && !FREE_COND_TS) - return -EWOULDBLOCK; - - while (todo >= TS_SIZE) { - if (!FREE_COND_TS) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(rb->queue, FREE_COND_TS)) - return count - todo; - } - if (copy_from_user(kb, buf, TS_SIZE)) - return -EFAULT; - write_ts_to_decoder(av7110, type, kb, TS_SIZE); - todo -= TS_SIZE; - buf += TS_SIZE; - } - - return count - todo; -} - - -#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ - dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) - -static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - - if (nonblock && !FREE_COND) - return -EWOULDBLOCK; - - while (todo > 0) { - if (!FREE_COND) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->avout.queue, - FREE_COND)) - return count - todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - if (copy_from_user(av7110->kbuf[type], buf, n)) - return -EFAULT; - av7110_ipack_instant_repack(av7110->kbuf[type], n, - &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - - if (nonblock && !FREE_COND) - return -EWOULDBLOCK; - - while (todo > 0) { - if (!FREE_COND) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->avout.queue, - FREE_COND)) - return count - todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, - unsigned long count, int nonblock, int type) -{ - unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); - - if (!av7110->kbuf[type]) - return -ENOBUFS; - if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) - return -EWOULDBLOCK; - - while (todo > 0) { - if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { - if (nonblock) - return count - todo; - if (wait_event_interruptible(av7110->aout.queue, - (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) - return count-todo; - } - n = todo; - if (n > IPACKS * 2) - n = IPACKS * 2; - if (copy_from_user(av7110->kbuf[type], buf, n)) - return -EFAULT; - av7110_ipack_instant_repack(av7110->kbuf[type], n, - &av7110->ipack[type]); - todo -= n; - buf += n; - } - return count - todo; -} - -void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) -{ - memset(p->pes, 0, TS_SIZE); - p->counter = 0; - p->pos = 0; - p->frags = 0; - if (feed) - p->feed = feed; -} - -static void clear_p2t(struct av7110_p2t *p) -{ - memset(p->pes, 0, TS_SIZE); -// p->counter = 0; - p->pos = 0; - p->frags = 0; -} - - -static int find_pes_header(u8 const *buf, long int length, int *frags) -{ - int c = 0; - int found = 0; - - *frags = 0; - - while (c < length - 3 && !found) { - if (buf[c] == 0x00 && buf[c + 1] == 0x00 && - buf[c + 2] == 0x01) { - switch ( buf[c + 3] ) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM: - case EMM_STREAM: - case PADDING_STREAM: - case DSM_CC_STREAM: - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - found = 1; - break; - - default: - c++; - break; - } - } else - c++; - } - if (c == length - 3 && !found) { - if (buf[length - 1] == 0x00) - *frags = 1; - if (buf[length - 2] == 0x00 && - buf[length - 1] == 0x00) - *frags = 2; - if (buf[length - 3] == 0x00 && - buf[length - 2] == 0x00 && - buf[length - 1] == 0x01) - *frags = 3; - return -1; - } - - return c; -} - -void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) -{ - int c, c2, l, add; - int check, rest; - - c = 0; - c2 = 0; - if (p->frags){ - check = 0; - switch(p->frags) { - case 1: - if (buf[c] == 0x00 && buf[c + 1] == 0x01) { - check = 1; - c += 2; - } - break; - case 2: - if (buf[c] == 0x01) { - check = 1; - c++; - } - break; - case 3: - check = 1; - } - if (check) { - switch (buf[c]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM: - case EMM_STREAM: - case PADDING_STREAM: - case DSM_CC_STREAM: - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - p->pes[0] = 0x00; - p->pes[1] = 0x00; - p->pes[2] = 0x01; - p->pes[3] = buf[c]; - p->pos = 4; - memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); - c += (TS_SIZE - 4) - p->pos; - p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); - clear_p2t(p); - break; - - default: - c = 0; - break; - } - } - p->frags = 0; - } - - if (p->pos) { - c2 = find_pes_header(buf + c, length - c, &p->frags); - if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) - l = c2+c; - else - l = (TS_SIZE - 4) - p->pos; - memcpy(p->pes + p->pos, buf, l); - c += l; - p->pos += l; - p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); - clear_p2t(p); - } - - add = 0; - while (c < length) { - c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); - if (c2 >= 0) { - c2 += c + add; - if (c2 > c){ - p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); - c = c2; - clear_p2t(p); - add = 0; - } else - add = 1; - } else { - l = length - c; - rest = l % (TS_SIZE - 4); - l -= rest; - p_to_t(buf + c, l, pid, &p->counter, p->feed); - memcpy(p->pes, buf + c + l, rest); - p->pos = rest; - c = length; - } - } -} - - -static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) -{ - int i; - int c = 0; - int fill; - u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; - - fill = (TS_SIZE - 4) - length; - if (pes_start) - tshead[1] = 0x40; - if (fill) - tshead[3] = 0x30; - tshead[1] |= (u8)((pid & 0x1F00) >> 8); - tshead[2] |= (u8)(pid & 0x00FF); - tshead[3] |= ((*counter)++ & 0x0F); - memcpy(buf, tshead, 4); - c += 4; - - if (fill) { - buf[4] = fill - 1; - c++; - if (fill > 1) { - buf[5] = 0x00; - c++; - } - for (i = 6; i < fill + 4; i++) { - buf[i] = 0xFF; - c++; - } - } - - return c; -} - - -static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, - struct dvb_demux_feed *feed) -{ - int l, pes_start; - u8 obuf[TS_SIZE]; - long c = 0; - - pes_start = 0; - if (length > 3 && - buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) - switch (buf[3]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM: - case EMM_STREAM: - case PADDING_STREAM: - case DSM_CC_STREAM: - case ISO13522_STREAM: - case PRIVATE_STREAM1: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - pes_start = 1; - break; - - default: - break; - } - - while (c < length) { - memset(obuf, 0, TS_SIZE); - if (length - c >= (TS_SIZE - 4)){ - l = write_ts_header2(pid, counter, pes_start, - obuf, (TS_SIZE - 4)); - memcpy(obuf + l, buf + c, TS_SIZE - l); - c += TS_SIZE - l; - } else { - l = write_ts_header2(pid, counter, pes_start, - obuf, length - c); - memcpy(obuf + l, buf + c, TS_SIZE - l); - c = length; - } - feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, NULL); - pes_start = 0; - } -} - - -static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) -{ - struct ipack *ipack = &av7110->ipack[type]; - - if (buf[1] & TRANS_ERROR) { - av7110_ipack_reset(ipack); - return -1; - } - - if (!(buf[3] & PAYLOAD)) - return -1; - - if (buf[1] & PAY_START) - av7110_ipack_flush(ipack); - - if (buf[3] & ADAPT_FIELD) { - len -= buf[4] + 1; - buf += buf[4] + 1; - if (!len) - return 0; - } - - av7110_ipack_instant_repack(buf + 4, len - 4, ipack); - return 0; -} - - -int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) -{ - struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) - return 0; - - switch (feed->pes_type) { - case 0: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - return -EINVAL; - break; - case 1: - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) - return -EINVAL; - break; - default: - return -1; - } - - return write_ts_to_decoder(av7110, feed->pes_type, buf, len); -} - - - -/****************************************************************************** - * Video MPEG decoder events - ******************************************************************************/ -void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) -{ - struct dvb_video_events *events = &av7110->video_events; - int wp; - - spin_lock_bh(&events->lock); - - wp = (events->eventw + 1) % MAX_VIDEO_EVENT; - if (wp == events->eventr) { - events->overflow = 1; - events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; - } - - //FIXME: timestamp? - memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); - events->eventw = wp; - - spin_unlock_bh(&events->lock); - - wake_up_interruptible(&events->wait_queue); -} - - -static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) -{ - struct dvb_video_events *events = &av7110->video_events; - - if (events->overflow) { - events->overflow = 0; - return -EOVERFLOW; - } - if (events->eventw == events->eventr) { - int ret; - - if (flags & O_NONBLOCK) - return -EWOULDBLOCK; - - ret = wait_event_interruptible(events->wait_queue, - events->eventw != events->eventr); - if (ret < 0) - return ret; - } - - spin_lock_bh(&events->lock); - - memcpy(event, &events->events[events->eventr], - sizeof(struct video_event)); - events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; - - spin_unlock_bh(&events->lock); - - return 0; -} - -/****************************************************************************** - * DVB device file operations - ******************************************************************************/ - -static __poll_t dvb_video_poll(struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - __poll_t mask = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) - poll_wait(file, &av7110->avout.queue, wait); - - poll_wait(file, &av7110->video_events.wait_queue, wait); - - if (av7110->video_events.eventw != av7110->video_events.eventr) - mask = EPOLLPRI; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - if (av7110->playing) { - if (FREE_COND) - mask |= (EPOLLOUT | EPOLLWRNORM); - } else { - /* if not playing: may play if asked for */ - mask |= (EPOLLOUT | EPOLLWRNORM); - } - } - - return mask; -} - -static ssize_t dvb_video_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned char c; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EPERM; - - if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) - return -EPERM; - - if (get_user(c, buf)) - return -EFAULT; - if (c == 0x47 && count % TS_SIZE == 0) - return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); - else - return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); -} - -static __poll_t dvb_audio_poll(struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - __poll_t mask = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - poll_wait(file, &av7110->aout.queue, wait); - - if (av7110->playing) { - if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) - mask |= (EPOLLOUT | EPOLLWRNORM); - } else /* if not playing: may play if asked for */ - mask = (EPOLLOUT | EPOLLWRNORM); - - return mask; -} - -static ssize_t dvb_audio_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned char c; - - dprintk(2, "av7110:%p, \n", av7110); - - if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { - printk(KERN_ERR "not audio source memory\n"); - return -EPERM; - } - - if (get_user(c, buf)) - return -EFAULT; - if (c == 0x47 && count % TS_SIZE == 0) - return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); - else - return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); -} - -static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; - -#define MIN_IFRAME 400000 - -static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) -{ - unsigned i, n; - int progressive = 0; - int match = 0; - - dprintk(2, "av7110:%p, \n", av7110); - - if (len == 0) - return 0; - - if (!(av7110->playing & RP_VIDEO)) { - if (av7110_av_start_play(av7110, RP_VIDEO) < 0) - return -EBUSY; - } - - /* search in buf for instances of 00 00 01 b5 1? */ - for (i = 0; i < len; i++) { - unsigned char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (match == 5) { - progressive = c & 0x08; - match = 0; - } - if (c == 0x00) { - match = (match == 1 || match == 2) ? 2 : 1; - continue; - } - switch (match++) { - case 2: if (c == 0x01) - continue; - break; - case 3: if (c == 0xb5) - continue; - break; - case 4: if ((c & 0xf0) == 0x10) - continue; - break; - } - match = 0; - } - - /* setting n always > 1, fixes problems when playing stillframes - consisting of I- and P-Frames */ - n = MIN_IFRAME / len + 1; - - /* FIXME: nonblock? */ - dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); - - for (i = 0; i < n; i++) - dvb_play(av7110, buf, len, 0, 1); - - av7110_ipack_flush(&av7110->ipack[1]); - - if (progressive) - return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); - else - return 0; -} - -#ifdef CONFIG_COMPAT -struct compat_video_still_picture { - compat_uptr_t iFrame; - int32_t size; -}; -#define VIDEO_STILLPICTURE32 _IOW('o', 30, struct compat_video_still_picture) - -struct compat_video_event { - __s32 type; - /* unused, make sure to use atomic time for y2038 if it ever gets used */ - compat_long_t timestamp; - union { - video_size_t size; - unsigned int frame_rate; /* in frames per 1000sec */ - unsigned char vsync_field; /* unknown/odd/even/progressive */ - } u; -}; -#define VIDEO_GET_EVENT32 _IOR('o', 28, struct compat_video_event) - -static int dvb_compat_video_get_event(struct av7110 *av7110, - struct compat_video_event *event, int flags) -{ - struct video_event ev; - int ret; - - ret = dvb_video_get_event(av7110, &ev, flags); - - *event = (struct compat_video_event) { - .type = ev.type, - .timestamp = ev.timestamp, - .u.size = ev.u.size, - }; - - return ret; -} -#endif - -static int dvb_video_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - int ret = 0; - - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); - - if ((file->f_flags & O_ACCMODE) == O_RDONLY) { - if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && - cmd != VIDEO_GET_SIZE ) { - return -EPERM; - } - } - - if (mutex_lock_interruptible(&av7110->ioctl_mutex)) - return -ERESTARTSYS; - - switch (cmd) { - case VIDEO_STOP: - av7110->videostate.play_state = VIDEO_STOPPED; - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) - ret = av7110_av_stop(av7110, RP_VIDEO); - else - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, - av7110->videostate.video_blank ? 0 : 1); - if (!ret) - av7110->trickmode = TRICK_NONE; - break; - - case VIDEO_PLAY: - av7110->trickmode = TRICK_NONE; - if (av7110->videostate.play_state == VIDEO_FREEZED) { - av7110->videostate.play_state = VIDEO_PLAYING; - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (ret) - break; - } - if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { - if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); - if (ret) - break; - av7110->playing &= ~RP_VIDEO; - } - ret = av7110_av_start_play(av7110, RP_VIDEO); - } - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) - av7110->videostate.play_state = VIDEO_PLAYING; - break; - - case VIDEO_FREEZE: - av7110->videostate.play_state = VIDEO_FREEZED; - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); - else - ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); - if (!ret) - av7110->trickmode = TRICK_FREEZE; - break; - - case VIDEO_CONTINUE: - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) { - av7110->videostate.play_state = VIDEO_PLAYING; - av7110->trickmode = TRICK_NONE; - } - break; - - case VIDEO_SELECT_SOURCE: - av7110->videostate.stream_source = (video_stream_source_t) arg; - break; - - case VIDEO_SET_BLANK: - av7110->videostate.video_blank = (int) arg; - break; - - case VIDEO_GET_STATUS: - memcpy(parg, &av7110->videostate, sizeof(struct video_status)); - break; - -#ifdef CONFIG_COMPAT - case VIDEO_GET_EVENT32: - ret = dvb_compat_video_get_event(av7110, parg, file->f_flags); - break; -#endif - - case VIDEO_GET_EVENT: - ret = dvb_video_get_event(av7110, parg, file->f_flags); - break; - - case VIDEO_GET_SIZE: - memcpy(parg, &av7110->video_size, sizeof(video_size_t)); - break; - - case VIDEO_SET_DISPLAY_FORMAT: - { - video_displayformat_t format = (video_displayformat_t) arg; - switch (format) { - case VIDEO_PAN_SCAN: - av7110->display_panscan = VID_PAN_SCAN_PREF; - break; - case VIDEO_LETTER_BOX: - av7110->display_panscan = VID_VC_AND_PS_PREF; - break; - case VIDEO_CENTER_CUT_OUT: - av7110->display_panscan = VID_CENTRE_CUT_PREF; - break; - default: - ret = -EINVAL; - } - if (ret < 0) - break; - av7110->videostate.display_format = format; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); - break; - } - - case VIDEO_SET_FORMAT: - if (arg > 1) { - ret = -EINVAL; - break; - } - av7110->display_ar = arg; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) arg); - break; - -#ifdef CONFIG_COMPAT - case VIDEO_STILLPICTURE32: - { - struct compat_video_still_picture *pic = - (struct compat_video_still_picture *) parg; - av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - ret = play_iframe(av7110, compat_ptr(pic->iFrame), - pic->size, file->f_flags & O_NONBLOCK); - break; - } -#endif - - case VIDEO_STILLPICTURE: - { - struct video_still_picture *pic = - (struct video_still_picture *) parg; - av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - ret = play_iframe(av7110, pic->iFrame, pic->size, - file->f_flags & O_NONBLOCK); - break; - } - - case VIDEO_FAST_FORWARD: - //note: arg is ignored by firmware - if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); - else - ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); - if (!ret) { - av7110->trickmode = TRICK_FAST; - av7110->videostate.play_state = VIDEO_PLAYING; - } - break; - - case VIDEO_SLOWMOTION: - if (av7110->playing&RP_VIDEO) { - if (av7110->trickmode != TRICK_SLOW) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } else { - ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } - if (!ret) { - av7110->trickmode = TRICK_SLOW; - av7110->videostate.play_state = VIDEO_PLAYING; - } - break; - - case VIDEO_GET_CAPABILITIES: - *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | - VIDEO_CAP_SYS | VIDEO_CAP_PROG; - break; - - case VIDEO_CLEAR_BUFFER: - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - av7110_ipack_reset(&av7110->ipack[1]); - if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); - if (ret) - break; - if (av7110->trickmode == TRICK_FAST) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); - if (av7110->trickmode == TRICK_SLOW) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Slow, 2, 0, 0); - if (!ret) - ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); - } - if (av7110->trickmode == TRICK_FREEZE) - ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); - } - break; - - case VIDEO_SET_STREAMTYPE: - break; - - default: - ret = -ENOIOCTLCMD; - break; - } - - mutex_unlock(&av7110->ioctl_mutex); - return ret; -} - -static int dvb_audio_ioctl(struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - int ret = 0; - - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); - - if (((file->f_flags & O_ACCMODE) == O_RDONLY) && - (cmd != AUDIO_GET_STATUS)) - return -EPERM; - - if (mutex_lock_interruptible(&av7110->ioctl_mutex)) - return -ERESTARTSYS; - - switch (cmd) { - case AUDIO_STOP: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - ret = av7110_av_stop(av7110, RP_AUDIO); - else - ret = audcom(av7110, AUDIO_CMD_MUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_STOPPED; - break; - - case AUDIO_PLAY: - if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) - ret = av7110_av_start_play(av7110, RP_AUDIO); - if (!ret) - ret = audcom(av7110, AUDIO_CMD_UNMUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_PLAYING; - break; - - case AUDIO_PAUSE: - ret = audcom(av7110, AUDIO_CMD_MUTE); - if (!ret) - av7110->audiostate.play_state = AUDIO_PAUSED; - break; - - case AUDIO_CONTINUE: - if (av7110->audiostate.play_state == AUDIO_PAUSED) { - av7110->audiostate.play_state = AUDIO_PLAYING; - ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); - } - break; - - case AUDIO_SELECT_SOURCE: - av7110->audiostate.stream_source = (audio_stream_source_t) arg; - break; - - case AUDIO_SET_MUTE: - { - ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); - if (!ret) - av7110->audiostate.mute_state = (int) arg; - break; - } - - case AUDIO_SET_AV_SYNC: - av7110->audiostate.AV_sync_state = (int) arg; - ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); - break; - - case AUDIO_SET_BYPASS_MODE: - if (FW_VERSION(av7110->arm_app) < 0x2621) - ret = -EINVAL; - av7110->audiostate.bypass_mode = (int)arg; - break; - - case AUDIO_CHANNEL_SELECT: - av7110->audiostate.channel_select = (audio_channel_select_t) arg; - switch(av7110->audiostate.channel_select) { - case AUDIO_STEREO: - ret = audcom(av7110, AUDIO_CMD_STEREO); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x49); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); - } - break; - case AUDIO_MONO_LEFT: - ret = audcom(av7110, AUDIO_CMD_MONO_L); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x4a); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); - } - break; - case AUDIO_MONO_RIGHT: - ret = audcom(av7110, AUDIO_CMD_MONO_R); - if (!ret) { - if (av7110->adac_type == DVB_ADAC_CRYSTAL) - i2c_writereg(av7110, 0x20, 0x02, 0x45); - else if (av7110->adac_type == DVB_ADAC_MSP34x5) - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); - } - break; - default: - ret = -EINVAL; - break; - } - break; - - case AUDIO_GET_STATUS: - memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); - break; - - case AUDIO_GET_CAPABILITIES: - if (FW_VERSION(av7110->arm_app) < 0x2621) - *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; - else - *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | - AUDIO_CAP_MP1 | AUDIO_CAP_MP2; - break; - - case AUDIO_CLEAR_BUFFER: - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - av7110_ipack_reset(&av7110->ipack[0]); - if (av7110->playing == RP_AV) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); - break; - - case AUDIO_SET_ID: - break; - - case AUDIO_SET_MIXER: - { - struct audio_mixer *amix = (struct audio_mixer *)parg; - ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); - break; - } - - case AUDIO_SET_STREAMTYPE: - break; - - default: - ret = -ENOIOCTLCMD; - } - - mutex_unlock(&av7110->ioctl_mutex); - return ret; -} - - -static int dvb_video_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((err = dvb_generic_open(inode, file)) < 0) - return err; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - av7110->video_blank = 1; - av7110->audiostate.AV_sync_state = 1; - av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; - - /* empty event queue */ - av7110->video_events.eventr = av7110->video_events.eventw = 0; - } - - return 0; -} - -static int dvb_video_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - av7110_av_stop(av7110, RP_VIDEO); - } - - return dvb_generic_release(inode, file); -} - -static int dvb_audio_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err = dvb_generic_open(inode, file); - - dprintk(2, "av7110:%p, \n", av7110); - - if (err < 0) - return err; - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; - return 0; -} - -static int dvb_audio_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(2, "av7110:%p, \n", av7110); - - av7110_av_stop(av7110, RP_AUDIO); - return dvb_generic_release(inode, file); -} - - - -/****************************************************************************** - * driver registration - ******************************************************************************/ - -static const struct file_operations dvb_video_fops = { - .owner = THIS_MODULE, - .write = dvb_video_write, - .unlocked_ioctl = dvb_generic_ioctl, - .compat_ioctl = dvb_generic_ioctl, - .open = dvb_video_open, - .release = dvb_video_release, - .poll = dvb_video_poll, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_video = { - .priv = NULL, - .users = 6, - .readers = 5, /* arbitrary */ - .writers = 1, - .fops = &dvb_video_fops, - .kernel_ioctl = dvb_video_ioctl, -}; - -static const struct file_operations dvb_audio_fops = { - .owner = THIS_MODULE, - .write = dvb_audio_write, - .unlocked_ioctl = dvb_generic_ioctl, - .compat_ioctl = dvb_generic_ioctl, - .open = dvb_audio_open, - .release = dvb_audio_release, - .poll = dvb_audio_poll, - .llseek = noop_llseek, -}; - -static struct dvb_device dvbdev_audio = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_audio_fops, - .kernel_ioctl = dvb_audio_ioctl, -}; - - -int av7110_av_register(struct av7110 *av7110) -{ - av7110->audiostate.AV_sync_state = 0; - av7110->audiostate.mute_state = 0; - av7110->audiostate.play_state = AUDIO_STOPPED; - av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; - av7110->audiostate.channel_select = AUDIO_STEREO; - av7110->audiostate.bypass_mode = 0; - - av7110->videostate.video_blank = 0; - av7110->videostate.play_state = VIDEO_STOPPED; - av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; - av7110->videostate.video_format = VIDEO_FORMAT_4_3; - av7110->videostate.display_format = VIDEO_LETTER_BOX; - av7110->display_ar = VIDEO_FORMAT_4_3; - av7110->display_panscan = VID_VC_AND_PS_PREF; - - init_waitqueue_head(&av7110->video_events.wait_queue); - spin_lock_init(&av7110->video_events.lock); - av7110->video_events.eventw = av7110->video_events.eventr = 0; - av7110->video_events.overflow = 0; - memset(&av7110->video_size, 0, sizeof (video_size_t)); - - dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, - &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0); - - dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, - &dvbdev_audio, av7110, DVB_DEVICE_AUDIO, 0); - - return 0; -} - -void av7110_av_unregister(struct av7110 *av7110) -{ - dvb_unregister_device(av7110->audio_dev); - dvb_unregister_device(av7110->video_dev); -} - -int av7110_av_init(struct av7110 *av7110) -{ - void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; - int i, ret; - - for (i = 0; i < 2; i++) { - struct ipack *ipack = av7110->ipack + i; - - ret = av7110_ipack_init(ipack, IPACKS, play[i]); - if (ret < 0) { - if (i) - av7110_ipack_free(--ipack); - goto out; - } - ipack->data = av7110; - } - - dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); - dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); - - av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); - av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; -out: - return ret; -} - -void av7110_av_exit(struct av7110 *av7110) -{ - av7110_ipack_free(&av7110->ipack[0]); - av7110_ipack_free(&av7110->ipack[1]); -} diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/av7110/av7110_av.h deleted file mode 100644 index 71bbd4391f57..000000000000 --- a/drivers/staging/media/av7110/av7110_av.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _AV7110_AV_H_ -#define _AV7110_AV_H_ - -struct av7110; - -extern int av7110_set_vidmode(struct av7110 *av7110, - enum av7110_video_mode mode); - -extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); -extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); -extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); - -extern int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, - unsigned int volright); -extern int av7110_av_stop(struct av7110 *av7110, int av); -extern int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed); -extern int av7110_av_start_play(struct av7110 *av7110, int av); - -extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); - -extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); -extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); - -extern int av7110_av_register(struct av7110 *av7110); -extern void av7110_av_unregister(struct av7110 *av7110); -extern int av7110_av_init(struct av7110 *av7110); -extern void av7110_av_exit(struct av7110 *av7110); - - -#endif /* _AV7110_AV_H_ */ diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c deleted file mode 100644 index c1338e074a3d..000000000000 --- a/drivers/staging/media/av7110/av7110_ca.c +++ /dev/null @@ -1,380 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * av7110_ca.c: CA and CI stuff - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * the project's page is at https://linuxtv.org - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_ca.h" - - -void CI_handle(struct av7110 *av7110, u8 *data, u16 len) -{ - dprintk(8, "av7110:%p\n",av7110); - - if (len < 3) - return; - switch (data[0]) { - case CI_MSG_CI_INFO: - if (data[2] != 1 && data[2] != 2) - break; - switch (data[1]) { - case 0: - av7110->ci_slot[data[2] - 1].flags = 0; - break; - case 1: - av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; - break; - case 2: - av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; - break; - } - break; - case CI_SWITCH_PRG_REPLY: - //av7110->ci_stat=data[1]; - break; - default: - break; - } -} - - -void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) -{ - if (dvb_ringbuffer_free(cibuf) < len + 2) - return; - - DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); - DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); - dvb_ringbuffer_write(cibuf, data, len); - wake_up_interruptible(&cibuf->queue); -} - - -/****************************************************************************** - * CI link layer file ops - ******************************************************************************/ - -static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) -{ - struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; - void *data; - - for (p = tab; *p; p++) { - data = vmalloc(size); - if (!data) { - while (p-- != tab) { - vfree(p[0]->data); - p[0]->data = NULL; - } - return -ENOMEM; - } - dvb_ringbuffer_init(*p, data, size); - } - return 0; -} - -static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) -{ - dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); - dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); -} - -static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) -{ - vfree(cirbuf->data); - cirbuf->data = NULL; - vfree(ciwbuf->data); - ciwbuf->data = NULL; -} - -static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, - int slots, struct ca_slot_info *slot) -{ - int i; - int len = 0; - u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; - - for (i = 0; i < 2; i++) { - if (slots & (1 << i)) - len += 8; - } - - if (dvb_ringbuffer_free(cibuf) < len) - return -EBUSY; - - for (i = 0; i < 2; i++) { - if (slots & (1 << i)) { - msg[2] = i; - dvb_ringbuffer_write(cibuf, msg, 8); - slot[i].flags = 0; - } - } - - return 0; -} - -static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - int free; - int non_blocking = file->f_flags & O_NONBLOCK; - u8 *page = (u8 *)__get_free_page(GFP_USER); - int res; - - if (!page) - return -ENOMEM; - - res = -EINVAL; - if (count > 2048) - goto out; - - res = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; - - free = dvb_ringbuffer_free(cibuf); - if (count + 2 > free) { - res = -EWOULDBLOCK; - if (non_blocking) - goto out; - res = -ERESTARTSYS; - if (wait_event_interruptible(cibuf->queue, - (dvb_ringbuffer_free(cibuf) >= count + 2))) - goto out; - } - - DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); - DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); - - res = dvb_ringbuffer_write(cibuf, page, count); -out: - free_page((unsigned long)page); - return res; -} - -static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - int avail; - int non_blocking = file->f_flags & O_NONBLOCK; - ssize_t len; - - if (!cibuf->data || !count) - return 0; - if (non_blocking && (dvb_ringbuffer_empty(cibuf))) - return -EWOULDBLOCK; - if (wait_event_interruptible(cibuf->queue, - !dvb_ringbuffer_empty(cibuf))) - return -ERESTARTSYS; - avail = dvb_ringbuffer_avail(cibuf); - if (avail < 4) - return 0; - len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; - len |= DVB_RINGBUFFER_PEEK(cibuf, 1); - if (avail < len + 2 || count < len) - return -EINVAL; - DVB_RINGBUFFER_SKIP(cibuf, 2); - - return dvb_ringbuffer_read_user(cibuf, buf, len); -} - -static int dvb_ca_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - int err = dvb_generic_open(inode, file); - - dprintk(8, "av7110:%p\n",av7110); - - if (err < 0) - return err; - ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); - return 0; -} - -static __poll_t dvb_ca_poll (struct file *file, poll_table *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; - struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; - __poll_t mask = 0; - - dprintk(8, "av7110:%p\n",av7110); - - poll_wait(file, &rbuf->queue, wait); - poll_wait(file, &wbuf->queue, wait); - - if (!dvb_ringbuffer_empty(rbuf)) - mask |= (EPOLLIN | EPOLLRDNORM); - - if (dvb_ringbuffer_free(wbuf) > 1024) - mask |= (EPOLLOUT | EPOLLWRNORM); - - return mask; -} - -static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; - int ret = 0; - - dprintk(8, "av7110:%p\n",av7110); - - if (mutex_lock_interruptible(&av7110->ioctl_mutex)) - return -ERESTARTSYS; - - switch (cmd) { - case CA_RESET: - ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, - &av7110->ci_slot[0]); - break; - case CA_GET_CAP: - { - struct ca_caps cap; - - cap.slot_num = 2; - cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? - CA_CI_LINK : CA_CI) | CA_DESCR; - cap.descr_num = 16; - cap.descr_type = CA_ECD; - memcpy(parg, &cap, sizeof(cap)); - break; - } - - case CA_GET_SLOT_INFO: - { - struct ca_slot_info *info=(struct ca_slot_info *)parg; - - if (info->num < 0 || info->num > 1) { - mutex_unlock(&av7110->ioctl_mutex); - return -EINVAL; - } - av7110->ci_slot[info->num].num = info->num; - av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? - CA_CI_LINK : CA_CI; - memcpy(info, &av7110->ci_slot[info->num], sizeof(struct ca_slot_info)); - break; - } - - case CA_GET_MSG: - break; - - case CA_SEND_MSG: - break; - - case CA_GET_DESCR_INFO: - { - struct ca_descr_info info; - - info.num = 16; - info.type = CA_ECD; - memcpy(parg, &info, sizeof (info)); - break; - } - - case CA_SET_DESCR: - { - struct ca_descr *descr = (struct ca_descr*) parg; - - if (descr->index >= 16 || descr->parity > 1) { - mutex_unlock(&av7110->ioctl_mutex); - return -EINVAL; - } - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, - (descr->index<<8)|descr->parity, - (descr->cw[0]<<8)|descr->cw[1], - (descr->cw[2]<<8)|descr->cw[3], - (descr->cw[4]<<8)|descr->cw[5], - (descr->cw[6]<<8)|descr->cw[7]); - break; - } - - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&av7110->ioctl_mutex); - return ret; -} - -static ssize_t dvb_ca_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(8, "av7110:%p\n",av7110); - return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); -} - -static ssize_t dvb_ca_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct av7110 *av7110 = dvbdev->priv; - - dprintk(8, "av7110:%p\n",av7110); - return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); -} - -static const struct file_operations dvb_ca_fops = { - .owner = THIS_MODULE, - .read = dvb_ca_read, - .write = dvb_ca_write, - .unlocked_ioctl = dvb_generic_ioctl, - .open = dvb_ca_open, - .release = dvb_generic_release, - .poll = dvb_ca_poll, - .llseek = default_llseek, -}; - -static struct dvb_device dvbdev_ca = { - .priv = NULL, - .users = 1, - .writers = 1, - .fops = &dvb_ca_fops, - .kernel_ioctl = dvb_ca_ioctl, -}; - - -int av7110_ca_register(struct av7110 *av7110) -{ - return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, - &dvbdev_ca, av7110, DVB_DEVICE_CA, 0); -} - -void av7110_ca_unregister(struct av7110 *av7110) -{ - dvb_unregister_device(av7110->ca_dev); -} - -int av7110_ca_init(struct av7110* av7110) -{ - return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); -} - -void av7110_ca_exit(struct av7110* av7110) -{ - ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); -} diff --git a/drivers/staging/media/av7110/av7110_ca.h b/drivers/staging/media/av7110/av7110_ca.h deleted file mode 100644 index a6e3f2955730..000000000000 --- a/drivers/staging/media/av7110/av7110_ca.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _AV7110_CA_H_ -#define _AV7110_CA_H_ - -struct av7110; - -extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); -extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); - -extern int av7110_ca_register(struct av7110 *av7110); -extern void av7110_ca_unregister(struct av7110 *av7110); -extern int av7110_ca_init(struct av7110* av7110); -extern void av7110_ca_exit(struct av7110* av7110); - -#endif /* _AV7110_CA_H_ */ diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c deleted file mode 100644 index 93ca31e38ddd..000000000000 --- a/drivers/staging/media/av7110/av7110_hw.c +++ /dev/null @@ -1,1204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * av7110_hw.c: av7110 low level hardware access and firmware interface - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * the project's page is at https://linuxtv.org - */ - -/* for debugging ARM communication: */ -//#define COM_DEBUG - -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" - -#define _NOHANDSHAKE - -/* - * Max transfer size done by av7110_fw_cmd() - * - * The maximum size passed to this function is 6 bytes. The buffer also - * uses two additional ones for type and size. So, 8 bytes is enough. - */ -#define MAX_XFER_SIZE 8 - -/**************************************************************************** - * DEBI functions - ****************************************************************************/ - -/* This DEBI code is based on the Stradis driver - by Nathan Laredo */ - -int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, unsigned int count) -{ - struct saa7146_dev *dev = av7110->dev; - - if (count > 32764) { - printk("%s: invalid count %d\n", __func__, count); - return -1; - } - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done failed\n", __func__); - return -1; - } - saa7146_write(dev, DEBI_CONFIG, config); - if (count <= 4) /* immediate transfer */ - saa7146_write(dev, DEBI_AD, val); - else /* block transfer */ - saa7146_write(dev, DEBI_AD, av7110->debi_bus); - saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); - saa7146_write(dev, MC2, (2 << 16) | 2); - return 0; -} - -u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count) -{ - struct saa7146_dev *dev = av7110->dev; - u32 result = 0; - - if (count > 32764) { - printk("%s: invalid count %d\n", __func__, count); - return 0; - } - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #1 failed\n", __func__); - return 0; - } - saa7146_write(dev, DEBI_AD, av7110->debi_bus); - saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); - - saa7146_write(dev, DEBI_CONFIG, config); - saa7146_write(dev, MC2, (2 << 16) | 2); - if (count > 4) - return count; - if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #2 failed\n", __func__); - return 0; - } - - result = saa7146_read(dev, DEBI_AD); - result &= (0xffffffffUL >> ((4 - count) * 8)); - return result; -} - - - -/* av7110 ARM core boot stuff */ -#if 0 -void av7110_reset_arm(struct av7110 *av7110) -{ - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - ARM_ResetMailBox(av7110); - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_ready = 1; - dprintk(1, "reset ARM\n"); -} -#endif /* 0 */ - -static int waitdebi(struct av7110 *av7110, int adr, int state) -{ - int k; - - dprintk(4, "%p\n", av7110); - - for (k = 0; k < 100; k++) { - if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) - return 0; - udelay(5); - } - return -ETIMEDOUT; -} - -static int load_dram(struct av7110 *av7110, u32 *data, int len) -{ - int i; - int blocks, rest; - u32 base, bootblock = AV7110_BOOT_BLOCK; - - dprintk(4, "%p\n", av7110); - - blocks = len / AV7110_BOOT_MAX_SIZE; - rest = len % AV7110_BOOT_MAX_SIZE; - base = DRAM_START_CODE; - - for (i = 0; i < blocks; i++) { - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); - return -ETIMEDOUT; - } - dprintk(4, "writing DRAM block %d\n", i); - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); - bootblock ^= 0x1400; - iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - base += AV7110_BOOT_MAX_SIZE; - } - - if (rest > 0) { - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); - return -ETIMEDOUT; - } - if (rest > 4) - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); - else - mwdebi(av7110, DEBISWAB, bootblock, - ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); - - iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - } - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); - return -ETIMEDOUT; - } - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - - -/* we cannot write av7110 DRAM directly, so load a bootloader into - * the DPRAM which implements a simple boot protocol */ -int av7110_bootarm(struct av7110 *av7110) -{ - const struct firmware *fw; - const char *fw_name = "av7110/bootcode.bin"; - struct saa7146_dev *dev = av7110->dev; - u32 ret; - int i; - - dprintk(4, "%p\n", av7110); - - av7110->arm_ready = 0; - - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - /* enable DEBI */ - saa7146_write(av7110->dev, MC1, 0x08800880); - saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); - saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - /* test DEBI */ - iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); - /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ - iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); - - if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { - printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", - ret, 0x10325476); - return -1; - } - for (i = 0; i < 8192; i += 4) - iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); - dprintk(2, "debi test OK\n"); - - /* boot */ - dprintk(1, "load boot code\n"); - saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); - //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); - //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); - - ret = request_firmware(&fw, fw_name, &dev->pci->dev); - if (ret) { - printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", - fw_name); - return ret; - } - - mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); - release_firmware(fw); - iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); - - if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out\n"); - return -ETIMEDOUT; - } - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); - mdelay(1); - - dprintk(1, "load dram code\n"); - if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): load_dram() failed\n"); - return -1; - } - - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); - mdelay(1); - - dprintk(1, "load dpram code\n"); - mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); - - if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out after loading DRAM\n"); - return -ETIMEDOUT; - } - saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - //ARM_ClearIrq(av7110); - ARM_ResetMailBox(av7110); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_errors = 0; - av7110->arm_ready = 1; - return 0; -} -MODULE_FIRMWARE("av7110/bootcode.bin"); - -/**************************************************************************** - * DEBI command polling - ****************************************************************************/ - -int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) -{ - unsigned long start; - u32 stat; - int err; - - if (FW_VERSION(av7110->arm_app) <= 0x261c) { - /* not supported by old firmware */ - msleep(50); - return 0; - } - - /* new firmware */ - start = jiffies; - for (;;) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - mutex_unlock(&av7110->dcomlock); - if ((stat & flags) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", - __func__, stat & flags); - return -ETIMEDOUT; - } - msleep(1); - } - return 0; -} - -static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) -{ - int i; - unsigned long start; - char *type = NULL; - u16 flags[2] = {0, 0}; - u32 stat; - int err; - -// dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -ENXIO; - } - - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); - av7110->arm_errors++; - return -ETIMEDOUT; - } - msleep(1); - } - - if (FW_VERSION(av7110->arm_app) <= 0x261f) - wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); - -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - - switch ((buf[0] >> 8) & 0xff) { - case COMTYPE_PIDFILTER: - case COMTYPE_ENCODER: - case COMTYPE_REC_PLAY: - case COMTYPE_MPEGDECODER: - type = "MSG"; - flags[0] = GPMQOver; - flags[1] = GPMQFull; - break; - case COMTYPE_OSD: - type = "OSD"; - flags[0] = OSDQOver; - flags[1] = OSDQFull; - break; - case COMTYPE_MISC: - if (FW_VERSION(av7110->arm_app) >= 0x261d) { - type = "MSG"; - flags[0] = GPMQOver; - flags[1] = GPMQBusy; - } - break; - default: - break; - } - - if (type != NULL) { - /* non-immediate COMMAND type */ - start = jiffies; - for (;;) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & flags[0]) { - printk(KERN_ERR "%s: %s QUEUE overflow\n", - __func__, type); - return -1; - } - if ((stat & flags[1]) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", - __func__, type); - av7110->arm_errors++; - return -ETIMEDOUT; - } - msleep(1); - } - } - - for (i = 2; i < length; i++) - wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); - - if (length) - wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); - else - wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); - - wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); - - if (FW_VERSION(av7110->arm_app) <= 0x261f) - wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); - -#ifdef COM_DEBUG - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", - __func__, (buf[0] >> 8) & 0xff); - return -ETIMEDOUT; - } - msleep(1); - } - - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & GPMQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); - return -ENOSPC; - } - else if (stat & OSDQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); - return -ENOSPC; - } -#endif - - return 0; -} - -static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) -{ - int ret; - -// dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -1; - } - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - ret = __av7110_send_fw_cmd(av7110, buf, length); - mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", - __func__, ret); - return ret; -} - -int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) -{ - va_list args; - u16 buf[MAX_XFER_SIZE]; - int i, ret; - -// dprintk(4, "%p\n", av7110); - - if (2 + num > ARRAY_SIZE(buf)) { - printk(KERN_WARNING - "%s: %s len=%d is too big!\n", - KBUILD_MODNAME, __func__, num); - return -EINVAL; - } - - buf[0] = ((type << 8) | com); - buf[1] = num; - - if (num) { - va_start(args, num); - for (i = 0; i < num; i++) - buf[i + 2] = va_arg(args, u32); - va_end(args); - } - - ret = av7110_send_fw_cmd(av7110, buf, num + 2); - if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); - return ret; -} - -#if 0 -int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) -{ - int i, ret; - u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - for(i = 0; i < len && i < 32; i++) - { - if(i % 2 == 0) - cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; - else - cmd[(i / 2) + 2] |= buf[i]; - } - - ret = av7110_send_fw_cmd(av7110, cmd, 18); - if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); - return ret; -} -#endif /* 0 */ - -int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, - int request_buf_len, u16 *reply_buf, int reply_buf_len) -{ - int err; - s16 i; - unsigned long start; -#ifdef COM_DEBUG - u32 stat; -#endif - - dprintk(4, "%p\n", av7110); - - if (!av7110->arm_ready) { - dprintk(1, "arm not ready.\n"); - return -1; - } - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { - mutex_unlock(&av7110->dcomlock); - printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); - return err; - } - - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_FREE); - if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } -#ifdef _NOHANDSHAKE - msleep(1); -#endif - } - -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - -#ifdef COM_DEBUG - stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); - if (stat & GPMQOver) { - printk(KERN_ERR "%s: GPMQOver\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -1; - } - else if (stat & OSDQOver) { - printk(KERN_ERR "%s: OSDQOver\n", __func__); - mutex_unlock(&av7110->dcomlock); - return -1; - } -#endif - - for (i = 0; i < reply_buf_len; i++) - reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); - - mutex_unlock(&av7110->dcomlock); - return 0; -} - -static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) -{ - int ret; - ret = av7110_fw_request(av7110, &tag, 0, buf, length); - if (ret) - printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); - return ret; -} - - -/**************************************************************************** - * Firmware commands - ****************************************************************************/ - -/* get version of the firmware ROM, RTSL, video ucode and ARM application */ -int av7110_firmversion(struct av7110 *av7110) -{ - u16 buf[20]; - u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); - - dprintk(4, "%p\n", av7110); - - if (av7110_fw_query(av7110, tag, buf, 16)) { - printk("dvb-ttpci: failed to boot firmware @ card %d\n", - av7110->dvb_adapter.num); - return -EIO; - } - - av7110->arm_fw = (buf[0] << 16) + buf[1]; - av7110->arm_rtsl = (buf[2] << 16) + buf[3]; - av7110->arm_vid = (buf[4] << 16) + buf[5]; - av7110->arm_app = (buf[6] << 16) + buf[7]; - av7110->avtype = (buf[8] << 16) + buf[9]; - - printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", - av7110->dvb_adapter.num, av7110->arm_fw, - av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); - - /* print firmware capabilities */ - if (FW_CI_LL_SUPPORT(av7110->arm_app)) - printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", - av7110->dvb_adapter.num); - else - printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", - av7110->dvb_adapter.num); - - return 0; -} - - -int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) -{ - int i, ret; - u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - if (len > 10) - len = 10; - - buf[1] = len + 2; - buf[2] = len; - - if (burst != -1) - buf[3] = burst ? 0x01 : 0x00; - else - buf[3] = 0xffff; - - for (i = 0; i < len; i++) - buf[i + 4] = msg[i]; - - ret = av7110_send_fw_cmd(av7110, buf, 18); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); - return ret; -} - - -#ifdef CONFIG_DVB_AV7110_OSD - -static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); -} - -static inline int SetBlend_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u8 blending) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, - windownr, colordepth, index, blending); -} - -static inline int SetColor_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, - windownr, colordepth, index, colorhi, colorlo); -} - -static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, - u16 colorfg, u16 colorbg) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, - windownr, fontsize, colorfg, colorbg); -} - -static int FlushText(struct av7110 *av7110) -{ - unsigned long start; - int err; - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - start = jiffies; - while (1) { - err = time_after(jiffies, start + ARM_WAIT_OSD); - if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) - break; - if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } - mutex_unlock(&av7110->dcomlock); - return 0; -} - -static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) -{ - int i, ret; - unsigned long start; - int length = strlen(buf) + 1; - u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; - - if (mutex_lock_interruptible(&av7110->dcomlock)) - return -ERESTARTSYS; - - start = jiffies; - while (1) { - ret = time_after(jiffies, start + ARM_WAIT_OSD); - if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) - break; - if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#ifndef _NOHANDSHAKE - start = jiffies; - while (1) { - ret = time_after(jiffies, start + ARM_WAIT_SHAKE); - if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) - break; - if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", - __func__); - mutex_unlock(&av7110->dcomlock); - return -ETIMEDOUT; - } - msleep(1); - } -#endif - for (i = 0; i < length / 2; i++) - wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, - swab16(*(u16 *)(buf + 2 * i)), 2); - if (length & 1) - wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); - ret = __av7110_send_fw_cmd(av7110, cbuf, 5); - mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); - return ret; -} - -static inline int DrawLine(struct av7110 *av7110, u8 windownr, - u16 x, u16 y, u16 dx, u16 dy, u16 color) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, - windownr, x, y, dx, dy, color); -} - -static inline int DrawBlock(struct av7110 *av7110, u8 windownr, - u16 x, u16 y, u16 dx, u16 dy, u16 color) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, - windownr, x, y, dx, dy, color); -} - -static inline int HideWindow(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); -} - -static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); -} - -static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); -} - -static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); -} - -static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, - osd_raw_window_t disptype, - u16 width, u16 height) -{ - return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, - windownr, disptype, width, height); -} - - -static enum av7110_osd_palette_type bpp2pal[8] = { - Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit -}; -static osd_raw_window_t bpp2bit[8] = { - OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 -}; - -static inline int WaitUntilBmpLoaded(struct av7110 *av7110) -{ - int ret = wait_event_timeout(av7110->bmpq, - av7110->bmp_state != BMP_LOADING, 10*HZ); - if (ret == 0) { - printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", - ret, av7110->bmp_state); - av7110->bmp_state = BMP_NONE; - return -ETIMEDOUT; - } - return 0; -} - -static inline int LoadBitmap(struct av7110 *av7110, - u16 dx, u16 dy, int inc, u8 __user * data) -{ - u16 format; - int bpp; - int i; - int d, delta; - u8 c; - int ret; - - dprintk(4, "%p\n", av7110); - - format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; - - av7110->bmp_state = BMP_LOADING; - if (format == OSD_BITMAP8) { - bpp=8; delta = 1; - } else if (format == OSD_BITMAP4) { - bpp=4; delta = 2; - } else if (format == OSD_BITMAP2) { - bpp=2; delta = 4; - } else if (format == OSD_BITMAP1) { - bpp=1; delta = 8; - } else { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; - av7110->bmpp = 0; - if (av7110->bmplen > 32768) { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - for (i = 0; i < dy; i++) { - if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { - av7110->bmp_state = BMP_NONE; - return -EINVAL; - } - } - if (format != OSD_BITMAP8) { - for (i = 0; i < dx * dy / delta; i++) { - c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; - for (d = delta - 2; d >= 0; d--) { - c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] - << ((delta - d - 1) * bpp)); - ((u8 *)av7110->bmpbuf)[1024 + i] = c; - } - } - } - av7110->bmplen += 1024; - dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); - ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); - if (!ret) - ret = WaitUntilBmpLoaded(av7110); - return ret; -} - -static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) -{ - dprintk(4, "%p\n", av7110); - - return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); -} - -static inline int ReleaseBitmap(struct av7110 *av7110) -{ - dprintk(4, "%p\n", av7110); - - if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) - return -1; - if (av7110->bmp_state == BMP_LOADING) - dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); - av7110->bmp_state = BMP_NONE; - return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); -} - -static u32 RGB2YUV(u16 R, u16 G, u16 B) -{ - u16 y, u, v; - u16 Y, Cr, Cb; - - y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ - u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ - v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ - - Y = y / 256; - Cb = u / 16; - Cr = v / 16; - - return Cr | (Cb << 16) | (Y << 8); -} - -static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) -{ - int ret; - - u16 ch, cl; - u32 yuv; - - yuv = blend ? RGB2YUV(r,g,b) : 0; - cl = (yuv & 0xffff); - ch = ((yuv >> 16) & 0xffff); - ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], - color, ch, cl); - if (!ret) - ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], - color, ((blend >> 4) & 0x0f)); - return ret; -} - -static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) -{ - int i; - int length = last - first + 1; - - if (length * 4 > DATA_BUFF3_SIZE) - return -EINVAL; - - for (i = 0; i < length; i++) { - u32 color, blend, yuv; - - if (get_user(color, colors + i)) - return -EFAULT; - blend = (color & 0xF0000000) >> 4; - yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, - (color >> 16) & 0xFF) | blend : 0; - yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); - wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); - } - return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, - av7110->osdwin, - bpp2pal[av7110->osdbpp[av7110->osdwin]], - first, last); -} - -static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, - int x1, int y1, int inc, u8 __user * data) -{ - uint w, h, bpp, bpl, size, lpb, bnum, brest; - int i; - int rc,release_rc; - - w = x1 - x0 + 1; - h = y1 - y0 + 1; - if (inc <= 0) - inc = w; - if (w <= 0 || w > 720 || h <= 0 || h > 576) - return -EINVAL; - bpp = av7110->osdbpp[av7110->osdwin] + 1; - bpl = ((w * bpp + 7) & ~7) / 8; - size = h * bpl; - lpb = (32 * 1024) / bpl; - bnum = size / (lpb * bpl); - brest = size - bnum * lpb * bpl; - - if (av7110->bmp_state == BMP_LOADING) { - /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ - BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); - rc = WaitUntilBmpLoaded(av7110); - if (rc) - return rc; - /* just continue. This should work for all fw versions - * if bnum==1 && !brest && LoadBitmap was successful - */ - } - - rc = 0; - for (i = 0; i < bnum; i++) { - rc = LoadBitmap(av7110, w, lpb, inc, data); - if (rc) - break; - rc = BlitBitmap(av7110, x0, y0 + i * lpb); - if (rc) - break; - data += lpb * inc; - } - if (!rc && brest) { - rc = LoadBitmap(av7110, w, brest / bpl, inc, data); - if (!rc) - rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); - } - release_rc = ReleaseBitmap(av7110); - if (!rc) - rc = release_rc; - if (rc) - dprintk(1,"returns %d\n",rc); - return rc; -} - -int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) -{ - int ret; - - if (mutex_lock_interruptible(&av7110->osd_mutex)) - return -ERESTARTSYS; - - switch (dc->cmd) { - case OSD_Close: - ret = DestroyOSDWindow(av7110, av7110->osdwin); - break; - case OSD_Open: - av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; - ret = CreateOSDWindow(av7110, av7110->osdwin, - bpp2bit[av7110->osdbpp[av7110->osdwin]], - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); - if (ret) - break; - if (!dc->data) { - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (ret) - break; - ret = SetColorBlend(av7110, av7110->osdwin); - } - break; - case OSD_Show: - ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); - break; - case OSD_Hide: - ret = HideWindow(av7110, av7110->osdwin); - break; - case OSD_Clear: - ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); - break; - case OSD_Fill: - ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); - break; - case OSD_SetColor: - ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); - break; - case OSD_SetPalette: - if (FW_VERSION(av7110->arm_app) >= 0x2618) - ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); - else { - int i, len = dc->x0-dc->color+1; - u8 __user *colors = (u8 __user *)dc->data; - u8 r, g = 0, b = 0, blend = 0; - ret = 0; - for (i = 0; icolor + i, r, g, b, blend); - if (ret) - break; - } - } - break; - case OSD_SetPixel: - ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, 0, 0, dc->color); - break; - case OSD_SetRow: - dc->y1 = dc->y0; - fallthrough; - case OSD_SetBlock: - ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); - break; - case OSD_FillRow: - ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1-dc->x0+1, dc->y1, dc->color); - break; - case OSD_FillBlock: - ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); - break; - case OSD_Line: - ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); - break; - case OSD_Text: - { - char textbuf[240]; - - if (strncpy_from_user(textbuf, dc->data, 240) < 0) { - ret = -EFAULT; - break; - } - textbuf[239] = 0; - if (dc->x1 > 3) - dc->x1 = 3; - ret = SetFont(av7110, av7110->osdwin, dc->x1, - (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); - if (!ret) - ret = FlushText(av7110); - if (!ret) - ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); - break; - } - case OSD_SetWindow: - if (dc->x0 < 1 || dc->x0 > 7) - ret = -EINVAL; - else { - av7110->osdwin = dc->x0; - ret = 0; - } - break; - case OSD_MoveWindow: - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (!ret) - ret = SetColorBlend(av7110, av7110->osdwin); - break; - case OSD_OpenRaw: - if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { - ret = -EINVAL; - break; - } - if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) - av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; - else - av7110->osdbpp[av7110->osdwin] = 0; - ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); - if (ret) - break; - if (!dc->data) { - ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); - if (!ret) - ret = SetColorBlend(av7110, av7110->osdwin); - } - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&av7110->osd_mutex); - if (ret==-ERESTARTSYS) - dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); - else if (ret) - dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); - - return ret; -} - -int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) -{ - switch (cap->cmd) { - case OSD_CAP_MEMSIZE: - if (FW_4M_SDRAM(av7110->arm_app)) - cap->val = 1000000; - else - cap->val = 92000; - return 0; - default: - return -EINVAL; - } -} -#endif /* CONFIG_DVB_AV7110_OSD */ diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h deleted file mode 100644 index 6380d8950c69..000000000000 --- a/drivers/staging/media/av7110/av7110_hw.h +++ /dev/null @@ -1,496 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _AV7110_HW_H_ -#define _AV7110_HW_H_ - -#include "av7110.h" - -/* DEBI transfer mode defs */ - -#define DEBINOSWAP 0x000e0000 -#define DEBISWAB 0x001e0000 -#define DEBISWAP 0x002e0000 - -#define ARM_WAIT_FREE (HZ) -#define ARM_WAIT_SHAKE (HZ/5) -#define ARM_WAIT_OSD (HZ) - - -enum av7110_bootstate -{ - BOOTSTATE_BUFFER_EMPTY = 0, - BOOTSTATE_BUFFER_FULL = 1, - BOOTSTATE_AV7110_BOOT_COMPLETE = 2 -}; - -enum av7110_type_rec_play_format -{ RP_None, - AudioPES, - AudioMp2, - AudioPCM, - VideoPES, - AV_PES -}; - -enum av7110_osd_palette_type -{ - NoPalet = 0, /* No palette */ - Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ - Pal2Bit = 4, /* 4 colors for 2 bit palette */ - Pal4Bit = 16, /* 16 colors for 4 bit palette */ - Pal8Bit = 256 /* 256 colors for 16 bit palette */ -}; - -/* switch defines */ -#define SB_GPIO 3 -#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ -#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ -#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ - -#define FB_GPIO 1 -#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ -#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ -#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ - -enum av7110_video_output_mode -{ - NO_OUT = 0, /* disable analog output */ - CVBS_RGB_OUT = 1, - CVBS_YC_OUT = 2, - YC_OUT = 3 -}; - -/* firmware internal msg q status: */ -#define GPMQFull 0x0001 /* Main Message Queue Full */ -#define GPMQOver 0x0002 /* Main Message Queue Overflow */ -#define HPQFull 0x0004 /* High Priority Msg Queue Full */ -#define HPQOver 0x0008 -#define OSDQFull 0x0010 /* OSD Queue Full */ -#define OSDQOver 0x0020 -#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ -#define HPQBusy 0x0080 -#define OSDQBusy 0x0100 - -/* hw section filter flags */ -#define SECTION_EIT 0x01 -#define SECTION_SINGLE 0x00 -#define SECTION_CYCLE 0x02 -#define SECTION_CONTINUOS 0x04 -#define SECTION_MODE 0x06 -#define SECTION_IPMPE 0x0C /* size up to 4k */ -#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ -#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ - -#define PBUFSIZE_NONE 0x0000 -#define PBUFSIZE_1P 0x0100 -#define PBUFSIZE_2P 0x0200 -#define PBUFSIZE_1K 0x0300 -#define PBUFSIZE_2K 0x0400 -#define PBUFSIZE_4K 0x0500 -#define PBUFSIZE_8K 0x0600 -#define PBUFSIZE_16K 0x0700 -#define PBUFSIZE_32K 0x0800 - - -/* firmware command codes */ -enum av7110_osd_command { - WCreate, - WDestroy, - WMoveD, - WMoveA, - WHide, - WTop, - DBox, - DLine, - DText, - Set_Font, - SetColor, - SetBlend, - SetWBlend, - SetCBlend, - SetNonBlend, - LoadBmp, - BlitBmp, - ReleaseBmp, - SetWTrans, - SetWNoTrans, - Set_Palette -}; - -enum av7110_pid_command { - MultiPID, - VideoPID, - AudioPID, - InitFilt, - FiltError, - NewVersion, - CacheError, - AddPIDFilter, - DelPIDFilter, - Scan, - SetDescr, - SetIR, - FlushTSQueue -}; - -enum av7110_mpeg_command { - SelAudChannels -}; - -enum av7110_audio_command { - AudioDAC, - CabADAC, - ON22K, - OFF22K, - MainSwitch, - ADSwitch, - SendDiSEqC, - SetRegister, - SpdifSwitch -}; - -enum av7110_request_command { - AudioState, - AudioBuffState, - VideoState1, - VideoState2, - VideoState3, - CrashCounter, - ReqVersion, - ReqVCXO, - ReqRegister, - ReqSecFilterError, - ReqSTC -}; - -enum av7110_encoder_command { - SetVidMode, - SetTestMode, - LoadVidCode, - SetMonitorType, - SetPanScanType, - SetFreezeMode, - SetWSSConfig -}; - -enum av7110_rec_play_state { - __Record, - __Stop, - __Play, - __Pause, - __Slow, - __FF_IP, - __Scan_I, - __Continue -}; - -enum av7110_fw_cmd_misc { - AV7110_FW_VIDEO_ZOOM = 1, - AV7110_FW_VIDEO_COMMAND, - AV7110_FW_AUDIO_COMMAND -}; - -enum av7110_command_type { - COMTYPE_NOCOM, - COMTYPE_PIDFILTER, - COMTYPE_MPEGDECODER, - COMTYPE_OSD, - COMTYPE_BMP, - COMTYPE_ENCODER, - COMTYPE_AUDIODAC, - COMTYPE_REQUEST, - COMTYPE_SYSTEM, - COMTYPE_REC_PLAY, - COMTYPE_COMMON_IF, - COMTYPE_PID_FILTER, - COMTYPE_PES, - COMTYPE_TS, - COMTYPE_VIDEO, - COMTYPE_AUDIO, - COMTYPE_CI_LL, - COMTYPE_MISC = 0x80 -}; - -#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ -#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ -#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ -#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ -#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ - -/* MPEG video decoder commands */ -#define AV_VIDEO_CMD_STOP 0x000e -#define AV_VIDEO_CMD_PLAY 0x000d -#define AV_VIDEO_CMD_FREEZE 0x0102 -#define AV_VIDEO_CMD_FFWD 0x0016 -#define AV_VIDEO_CMD_SLOW 0x0022 - -/* MPEG audio decoder commands */ -#define AUDIO_CMD_MUTE 0x0001 -#define AUDIO_CMD_UNMUTE 0x0002 -#define AUDIO_CMD_PCM16 0x0010 -#define AUDIO_CMD_STEREO 0x0080 -#define AUDIO_CMD_MONO_L 0x0100 -#define AUDIO_CMD_MONO_R 0x0200 -#define AUDIO_CMD_SYNC_OFF 0x000e -#define AUDIO_CMD_SYNC_ON 0x000f - -/* firmware data interface codes */ -#define DATA_NONE 0x00 -#define DATA_FSECTION 0x01 -#define DATA_IPMPE 0x02 -#define DATA_MPEG_RECORD 0x03 -#define DATA_DEBUG_MESSAGE 0x04 -#define DATA_COMMON_INTERFACE 0x05 -#define DATA_MPEG_PLAY 0x06 -#define DATA_BMP_LOAD 0x07 -#define DATA_IRCOMMAND 0x08 -#define DATA_PIPING 0x09 -#define DATA_STREAMING 0x0a -#define DATA_CI_GET 0x0b -#define DATA_CI_PUT 0x0c -#define DATA_MPEG_VIDEO_EVENT 0x0d - -#define DATA_PES_RECORD 0x10 -#define DATA_PES_PLAY 0x11 -#define DATA_TS_RECORD 0x12 -#define DATA_TS_PLAY 0x13 - -/* ancient CI command codes, only two are actually still used - * by the link level CI firmware */ -#define CI_CMD_ERROR 0x00 -#define CI_CMD_ACK 0x01 -#define CI_CMD_SYSTEM_READY 0x02 -#define CI_CMD_KEYPRESS 0x03 -#define CI_CMD_ON_TUNED 0x04 -#define CI_CMD_ON_SWITCH_PROGRAM 0x05 -#define CI_CMD_SECTION_ARRIVED 0x06 -#define CI_CMD_SECTION_TIMEOUT 0x07 -#define CI_CMD_TIME 0x08 -#define CI_CMD_ENTER_MENU 0x09 -#define CI_CMD_FAST_PSI 0x0a -#define CI_CMD_GET_SLOT_INFO 0x0b - -#define CI_MSG_NONE 0x00 -#define CI_MSG_CI_INFO 0x01 -#define CI_MSG_MENU 0x02 -#define CI_MSG_LIST 0x03 -#define CI_MSG_TEXT 0x04 -#define CI_MSG_REQUEST_INPUT 0x05 -#define CI_MSG_INPUT_COMPLETE 0x06 -#define CI_MSG_LIST_MORE 0x07 -#define CI_MSG_MENU_MORE 0x08 -#define CI_MSG_CLOSE_MMI_IMM 0x09 -#define CI_MSG_SECTION_REQUEST 0x0a -#define CI_MSG_CLOSE_FILTER 0x0b -#define CI_PSI_COMPLETE 0x0c -#define CI_MODULE_READY 0x0d -#define CI_SWITCH_PRG_REPLY 0x0e -#define CI_MSG_TEXT_MORE 0x0f - -#define CI_MSG_CA_PMT 0xe0 -#define CI_MSG_ERROR 0xf0 - - -/* base address of the dual ported RAM which serves as communication - * area between PCI bus and av7110, - * as seen by the DEBI bus of the saa7146 */ -#define DPRAM_BASE 0x4000 - -/* boot protocol area */ -#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) -#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) -#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) -#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) -#define AV7110_BOOT_MAX_SIZE 0xc00 - -/* firmware command protocol area */ -#define IRQ_STATE (DPRAM_BASE + 0x0F4) -#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) -#define MSGSTATE (DPRAM_BASE + 0x0F8) -#define COMMAND (DPRAM_BASE + 0x0FC) -#define COM_BUFF (DPRAM_BASE + 0x100) -#define COM_BUFF_SIZE 0x20 - -/* various data buffers */ -#define BUFF1_BASE (DPRAM_BASE + 0x120) -#define BUFF1_SIZE 0xE0 - -#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) -#define DATA_BUFF0_SIZE 0x0800 - -#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) -#define DATA_BUFF1_SIZE 0x0800 - -#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) -#define DATA_BUFF2_SIZE 0x0800 - -#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) -#define DATA_BUFF3_SIZE 0x0400 - -#define Reserved (DPRAM_BASE + 0x1E00) -#define Reserved_SIZE 0x1C0 - - -/* firmware status area */ -#define STATUS_BASE (DPRAM_BASE + 0x1FC0) -#define STATUS_LOOPS (STATUS_BASE + 0x08) - -#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) -/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ -#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) - -/* firmware data protocol area */ -#define RX_TYPE (DPRAM_BASE + 0x1FE8) -#define RX_LEN (DPRAM_BASE + 0x1FEA) -#define TX_TYPE (DPRAM_BASE + 0x1FEC) -#define TX_LEN (DPRAM_BASE + 0x1FEE) - -#define RX_BUFF (DPRAM_BASE + 0x1FF4) -#define TX_BUFF (DPRAM_BASE + 0x1FF6) - -#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) -#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) - -#define IRQ_RX (DPRAM_BASE + 0x1FFC) -#define IRQ_TX (DPRAM_BASE + 0x1FFE) - -/* used by boot protocol to load firmware into av7110 DRAM */ -#define DRAM_START_CODE 0x2e000404 -#define DRAM_MAX_CODE_SIZE 0x00100000 - -/* saa7146 gpio lines */ -#define RESET_LINE 2 -#define DEBI_DONE_LINE 1 -#define ARM_IRQ_LINE 0 - - - -extern int av7110_bootarm(struct av7110 *av7110); -extern int av7110_firmversion(struct av7110 *av7110); -#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) -#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) -#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) - -extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); -extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); -extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, - int request_buf_len, u16 *reply_buf, int reply_buf_len); - - -/* DEBI (saa7146 data extension bus interface) access */ -extern int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, unsigned int count); -extern u32 av7110_debiread(struct av7110 *av7110, u32 config, - int addr, unsigned int count); - - -/* DEBI during interrupt */ -/* single word writes */ -static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) -{ - av7110_debiwrite(av7110, config, addr, val, count); -} - -/* buffer writes */ -static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, - const u8 *val, int count) -{ - memcpy(av7110->debi_virt, val, count); - av7110_debiwrite(av7110, config, addr, 0, count); -} - -static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) -{ - u32 res; - - res=av7110_debiread(av7110, config, addr, count); - if (count<=4) - memcpy(av7110->debi_virt, (char *) &res, count); - return res; -} - -/* DEBI outside interrupts, only for count <= 4! */ -static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) -{ - unsigned long flags; - - spin_lock_irqsave(&av7110->debilock, flags); - av7110_debiwrite(av7110, config, addr, val, count); - spin_unlock_irqrestore(&av7110->debilock, flags); -} - -static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) -{ - unsigned long flags; - u32 res; - - spin_lock_irqsave(&av7110->debilock, flags); - res=av7110_debiread(av7110, config, addr, count); - spin_unlock_irqrestore(&av7110->debilock, flags); - return res; -} - -/* handle mailbox registers of the dual ported RAM */ -static inline void ARM_ResetMailBox(struct av7110 *av7110) -{ - unsigned long flags; - - spin_lock_irqsave(&av7110->debilock, flags); - av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); - av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); - spin_unlock_irqrestore(&av7110->debilock, flags); -} - -static inline void ARM_ClearMailBox(struct av7110 *av7110) -{ - iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); -} - -static inline void ARM_ClearIrq(struct av7110 *av7110) -{ - irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); -} - -/**************************************************************************** - * Firmware commands - ****************************************************************************/ - -static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) -{ - return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); -} - -static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) -{ - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); -} - -static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) -{ - return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, - (com>>16), (com&0xffff), - (arg>>16), (arg&0xffff)); -} - -static inline int audcom(struct av7110 *av7110, u32 com) -{ - return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, - (com>>16), (com&0xffff)); -} - -static inline int Set22K(struct av7110 *av7110, int state) -{ - return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); -} - - -extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); - - -#ifdef CONFIG_DVB_AV7110_OSD -extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); -extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); -#endif /* CONFIG_DVB_AV7110_OSD */ - - - -#endif /* _AV7110_HW_H_ */ diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c deleted file mode 100644 index 30330ed01ce8..000000000000 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ /dev/null @@ -1,404 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "dvb_filter.h" -#include "av7110_ipack.h" -#include /* for memcpy() */ -#include - - -void av7110_ipack_reset(struct ipack *p) -{ - p->found = 0; - p->cid = 0; - p->plength = 0; - p->flag1 = 0; - p->flag2 = 0; - p->hlength = 0; - p->mpeg = 0; - p->check = 0; - p->which = 0; - p->done = 0; - p->count = 0; -} - - -int av7110_ipack_init(struct ipack *p, int size, - void (*func)(u8 *buf, int size, void *priv)) -{ - if (!(p->buf = vmalloc(size))) { - printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); - return -ENOMEM; - } - p->size = size; - p->func = func; - p->repack_subids = 0; - av7110_ipack_reset(p); - return 0; -} - - -void av7110_ipack_free(struct ipack *p) -{ - vfree(p->buf); -} - - -static void send_ipack(struct ipack *p) -{ - int off; - struct dvb_audio_info ai; - int ac3_off = 0; - int streamid = 0; - int nframes = 0; - int f = 0; - - switch (p->mpeg) { - case 2: - if (p->count < 10) - return; - p->buf[3] = p->cid; - p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); - p->buf[5] = (u8)((p->count - 6) & 0x00ff); - if (p->repack_subids && p->cid == PRIVATE_STREAM1) { - off = 9 + p->buf[8]; - streamid = p->buf[off]; - if ((streamid & 0xf8) == 0x80) { - ai.off = 0; - ac3_off = ((p->buf[off + 2] << 8)| - p->buf[off + 3]); - if (ac3_off < p->count) - f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, - p->count - ac3_off, &ai, 0); - if (!f) { - nframes = (p->count - off - 3 - ac3_off) / - ai.framesize + 1; - p->buf[off + 2] = (ac3_off >> 8) & 0xff; - p->buf[off + 3] = (ac3_off) & 0xff; - p->buf[off + 1] = nframes; - ac3_off += nframes * ai.framesize - p->count; - } - } - } - p->func(p->buf, p->count, p->data); - - p->buf[6] = 0x80; - p->buf[7] = 0x00; - p->buf[8] = 0x00; - p->count = 9; - if (p->repack_subids && p->cid == PRIVATE_STREAM1 - && (streamid & 0xf8) == 0x80) { - p->count += 4; - p->buf[9] = streamid; - p->buf[10] = (ac3_off >> 8) & 0xff; - p->buf[11] = (ac3_off) & 0xff; - p->buf[12] = 0; - } - break; - - case 1: - if (p->count < 8) - return; - p->buf[3] = p->cid; - p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); - p->buf[5] = (u8)((p->count - 6) & 0x00ff); - p->func(p->buf, p->count, p->data); - - p->buf[6] = 0x0f; - p->count = 7; - break; - } -} - - -void av7110_ipack_flush(struct ipack *p) -{ - if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) - return; - p->plength = p->found - 6; - p->found = 0; - send_ipack(p); - av7110_ipack_reset(p); -} - - -static void write_ipack(struct ipack *p, const u8 *data, int count) -{ - u8 headr[3] = { 0x00, 0x00, 0x01 }; - - if (p->count < 6) { - memcpy(p->buf, headr, 3); - p->count = 6; - } - - if (p->count + count < p->size){ - memcpy(p->buf+p->count, data, count); - p->count += count; - } else { - int rest = p->size - p->count; - memcpy(p->buf+p->count, data, rest); - p->count += rest; - send_ipack(p); - if (count - rest > 0) - write_ipack(p, data + rest, count - rest); - } -} - - -int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) -{ - int l; - int c = 0; - - while (c < count && (p->mpeg == 0 || - (p->mpeg == 1 && p->found < 7) || - (p->mpeg == 2 && p->found < 9)) - && (p->found < 5 || !p->done)) { - switch (p->found) { - case 0: - case 1: - if (buf[c] == 0x00) - p->found++; - else - p->found = 0; - c++; - break; - case 2: - if (buf[c] == 0x01) - p->found++; - else if (buf[c] == 0) - p->found = 2; - else - p->found = 0; - c++; - break; - case 3: - p->cid = 0; - switch (buf[c]) { - case PROG_STREAM_MAP: - case PRIVATE_STREAM2: - case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : - case ISO13522_STREAM: - p->done = 1; - fallthrough; - case PRIVATE_STREAM1: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - p->found++; - p->cid = buf[c]; - c++; - break; - default: - p->found = 0; - break; - } - break; - - case 4: - if (count-c > 1) { - p->plen[0] = buf[c]; - c++; - p->plen[1] = buf[c]; - c++; - p->found += 2; - p->plength = (p->plen[0] << 8) | p->plen[1]; - } else { - p->plen[0] = buf[c]; - p->found++; - return count; - } - break; - case 5: - p->plen[1] = buf[c]; - c++; - p->found++; - p->plength = (p->plen[0] << 8) | p->plen[1]; - break; - case 6: - if (!p->done) { - p->flag1 = buf[c]; - c++; - p->found++; - if ((p->flag1 & 0xc0) == 0x80) - p->mpeg = 2; - else { - p->hlength = 0; - p->which = 0; - p->mpeg = 1; - p->flag2 = 0; - } - } - break; - - case 7: - if (!p->done && p->mpeg == 2) { - p->flag2 = buf[c]; - c++; - p->found++; - } - break; - - case 8: - if (!p->done && p->mpeg == 2) { - p->hlength = buf[c]; - c++; - p->found++; - } - break; - } - } - - if (c == count) - return count; - - if (!p->plength) - p->plength = MMAX_PLENGTH - 6; - - if (p->done || ((p->mpeg == 2 && p->found >= 9) || - (p->mpeg == 1 && p->found >= 7))) { - switch (p->cid) { - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - case PRIVATE_STREAM1: - if (p->mpeg == 2 && p->found == 9) { - write_ipack(p, &p->flag1, 1); - write_ipack(p, &p->flag2, 1); - write_ipack(p, &p->hlength, 1); - } - - if (p->mpeg == 1 && p->found == 7) - write_ipack(p, &p->flag1, 1); - - if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && - p->found < 14) { - while (c < count && p->found < 14) { - p->pts[p->found - 9] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - } - if (c == count) - return count; - } - - if (p->mpeg == 1 && p->which < 2000) { - - if (p->found == 7) { - p->check = p->flag1; - p->hlength = 1; - } - - while (!p->which && c < count && - p->check == 0xff){ - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - } - - if (c == count) - return count; - - if ((p->check & 0xc0) == 0x40 && !p->which) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - - p->which = 1; - if (c == count) - return count; - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; - if (c == count) - return count; - } - - if (p->which == 1) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; - if (c == count) - return count; - } - - if ((p->check & 0x30) && p->check != 0xff) { - p->flag2 = (p->check & 0xf0) << 2; - p->pts[0] = p->check; - p->which = 3; - } - - if (c == count) - return count; - if (p->which > 2){ - if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { - while (c < count && p->which < 7) { - p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; - } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { - while (c < count && p->which < 12) { - if (p->which < 7) - p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; - } - p->which = 2000; - } - - } - - while (c < count && p->found < p->plength + 6) { - l = count - c; - if (l + p->found > p->plength + 6) - l = p->plength + 6 - p->found; - write_ipack(p, buf + c, l); - p->found += l; - c += l; - } - break; - } - - - if (p->done) { - if (p->found + count - c < p->plength + 6) { - p->found += count - c; - c = count; - } else { - c += p->plength + 6 - p->found; - p->found = p->plength + 6; - } - } - - if (p->plength && p->found == p->plength + 6) { - send_ipack(p); - av7110_ipack_reset(p); - if (c < count) - av7110_ipack_instant_repack(buf + c, count - c, p); - } - } - return count; -} diff --git a/drivers/staging/media/av7110/av7110_ipack.h b/drivers/staging/media/av7110/av7110_ipack.h deleted file mode 100644 index 943ec899bb93..000000000000 --- a/drivers/staging/media/av7110/av7110_ipack.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _AV7110_IPACK_H_ -#define _AV7110_IPACK_H_ - -extern int av7110_ipack_init(struct ipack *p, int size, - void (*func)(u8 *buf, int size, void *priv)); - -extern void av7110_ipack_reset(struct ipack *p); -extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); -extern void av7110_ipack_free(struct ipack * p); -extern void av7110_ipack_flush(struct ipack *p); - -#endif diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c deleted file mode 100644 index a851ba328e4a..000000000000 --- a/drivers/staging/media/av7110/av7110_ir.c +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for the remote control of SAA7146 based AV7110 cards - * - * Copyright (C) 1999-2003 Holger Waechtler - * Copyright (C) 2003-2007 Oliver Endriss - * Copyright (C) 2019 Sean Young - */ - -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" - -#define IR_RC5 0 -#define IR_RCMM 1 -#define IR_RC5_EXT 2 /* internal only */ - -/* interrupt handler */ -void av7110_ir_handler(struct av7110 *av7110, u32 ircom) -{ - struct rc_dev *rcdev = av7110->ir.rcdev; - enum rc_proto proto; - u32 command, addr, scancode; - u32 toggle; - - dprintk(4, "ir command = %08x\n", ircom); - - if (rcdev) { - switch (av7110->ir.ir_config) { - case IR_RC5: /* RC5: 5 bits device address, 6 bits command */ - command = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - scancode = RC_SCANCODE_RC5(addr, command); - toggle = ircom & 0x0800; - proto = RC_PROTO_RC5; - break; - - case IR_RCMM: /* RCMM: 32 bits scancode */ - scancode = ircom & ~0x8000; - toggle = ircom & 0x8000; - proto = RC_PROTO_RCMM32; - break; - - case IR_RC5_EXT: - /* - * extended RC5: 5 bits device address, 7 bits command - * - * Extended RC5 uses only one start bit. The second - * start bit is re-assigned bit 6 of the command bit. - */ - command = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - if (!(ircom & 0x1000)) - command |= 0x40; - scancode = RC_SCANCODE_RC5(addr, command); - toggle = ircom & 0x0800; - proto = RC_PROTO_RC5; - break; - default: - dprintk(2, "unknown ir config %d\n", - av7110->ir.ir_config); - return; - } - - rc_keydown(rcdev, proto, scancode, toggle != 0); - } -} - -int av7110_set_ir_config(struct av7110 *av7110) -{ - dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); - - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); -} - -static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) -{ - struct av7110 *av7110 = rcdev->priv; - u32 ir_config; - - if (*rc_type & RC_PROTO_BIT_RCMM32) { - ir_config = IR_RCMM; - *rc_type = RC_PROTO_BIT_RCMM32; - } else if (*rc_type & RC_PROTO_BIT_RC5) { - if (FW_VERSION(av7110->arm_app) >= 0x2620) - ir_config = IR_RC5_EXT; - else - ir_config = IR_RC5; - *rc_type = RC_PROTO_BIT_RC5; - } else { - return -EINVAL; - } - - if (ir_config == av7110->ir.ir_config) - return 0; - - av7110->ir.ir_config = ir_config; - - return av7110_set_ir_config(av7110); -} - -int av7110_ir_init(struct av7110 *av7110) -{ - struct rc_dev *rcdev; - struct pci_dev *pci; - int ret; - - rcdev = rc_allocate_device(RC_DRIVER_SCANCODE); - if (!rcdev) - return -ENOMEM; - - pci = av7110->dev->pci; - - snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), - "pci-%s/ir0", pci_name(pci)); - - rcdev->device_name = av7110->card_name; - rcdev->driver_name = KBUILD_MODNAME; - rcdev->input_phys = av7110->ir.input_phys; - rcdev->input_id.bustype = BUS_PCI; - rcdev->input_id.version = 2; - if (pci->subsystem_vendor) { - rcdev->input_id.vendor = pci->subsystem_vendor; - rcdev->input_id.product = pci->subsystem_device; - } else { - rcdev->input_id.vendor = pci->vendor; - rcdev->input_id.product = pci->device; - } - - rcdev->dev.parent = &pci->dev; - rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32; - rcdev->change_protocol = change_protocol; - rcdev->map_name = RC_MAP_HAUPPAUGE; - rcdev->priv = av7110; - - av7110->ir.rcdev = rcdev; - av7110->ir.ir_config = IR_RC5; - av7110_set_ir_config(av7110); - - ret = rc_register_device(rcdev); - if (ret) { - av7110->ir.rcdev = NULL; - rc_free_device(rcdev); - } - - return ret; -} - -void av7110_ir_exit(struct av7110 *av7110) -{ - rc_unregister_device(av7110->ir.rcdev); -} - -//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); -//MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c deleted file mode 100644 index c89f536f699c..000000000000 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ /dev/null @@ -1,952 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module - * - * Copyright (C) 1999-2002 Ralph Metzler - * & Marcus Metzler for convergence integrated media GmbH - * - * originally based on code by: - * Copyright (C) 1998,1999 Christian Theiss - * - * the project's page is at https://linuxtv.org - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "av7110.h" -#include "av7110_hw.h" -#include "av7110_av.h" - -int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) -{ - u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; - struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; - - switch (av7110->adac_type) { - case DVB_ADAC_MSP34x0: - msgs.addr = 0x40; - break; - case DVB_ADAC_MSP34x5: - msgs.addr = 0x42; - break; - default: - return 0; - } - - if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", - av7110->dvb_adapter.num, reg, val); - return -EIO; - } - return 0; -} - -static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) -{ - u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; - u8 msg2[2]; - struct i2c_msg msgs[2] = { - { .flags = 0 , .len = 3, .buf = msg1 }, - { .flags = I2C_M_RD, .len = 2, .buf = msg2 } - }; - - switch (av7110->adac_type) { - case DVB_ADAC_MSP34x0: - msgs[0].addr = 0x40; - msgs[1].addr = 0x40; - break; - case DVB_ADAC_MSP34x5: - msgs[0].addr = 0x42; - msgs[1].addr = 0x42; - break; - default: - return 0; - } - - if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", - av7110->dvb_adapter.num, reg); - return -EIO; - } - *val = (msg2[0] << 8) | msg2[1]; - return 0; -} - -static struct v4l2_input inputs[4] = { - { - .index = 0, - .name = "DVB", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 1, - .tuner = 0, /* ignored */ - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 1, - .name = "Television", - .type = V4L2_INPUT_TYPE_TUNER, - .audioset = 1, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 2, - .name = "Video", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 0, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - }, { - .index = 3, - .name = "Y/C", - .type = V4L2_INPUT_TYPE_CAMERA, - .audioset = 0, - .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, - .status = 0, - .capabilities = V4L2_IN_CAP_STD, - } -}; - -static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) -{ - struct av7110 *av7110 = dev->ext_priv; - u8 buf[] = { 0x00, reg, data }; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; - - dprintk(4, "dev: %p\n", dev); - - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) - return -1; - return 0; -} - -static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) -{ - struct av7110 *av7110 = dev->ext_priv; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; - - dprintk(4, "dev: %p\n", dev); - - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) - return -1; - return 0; -} - -static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) -{ - u32 div; - u8 config; - u8 buf[4]; - - dprintk(4, "freq: 0x%08x\n", freq); - - /* magic number: 614. tuning with the frequency given by v4l2 - is always off by 614*62.5 = 38375 kHz...*/ - div = freq + 614; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x8e; - - if (freq < 16U * 16825 / 100) - config = 0xa0; - else if (freq < 16U * 44725 / 100) - config = 0x90; - else - config = 0x30; - config &= ~0x02; - - buf[3] = config; - - return tuner_write(dev, 0x61, buf); -} - -static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) -{ - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; - u32 div; - u8 data[4]; - - div = (freq + 38900000 + 31250) / 62500; - - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0xce; - - if (freq < 45000000) - return -EINVAL; - else if (freq < 137000000) - data[3] = 0x01; - else if (freq < 403000000) - data[3] = 0x02; - else if (freq < 860000000) - data[3] = 0x04; - else - return -EINVAL; - - if (av7110->fe->ops.i2c_gate_ctrl) - av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); - return tuner_write(dev, 0x63, data); -} - - - -static struct saa7146_standard analog_standard[]; -static struct saa7146_standard dvb_standard[]; -static struct saa7146_standard standard[]; - -static const struct v4l2_audio msp3400_v4l2_audio = { - .index = 0, - .name = "Television", - .capability = V4L2_AUDCAP_STEREO -}; - -static int av7110_dvb_c_switch(struct saa7146_fh *fh) -{ - struct saa7146_dev *dev = fh->dev; - struct saa7146_vv *vv = dev->vv_data; - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; - u16 adswitch; - int source, sync, err; - - dprintk(4, "%p\n", av7110); - - if ((vv->video_status & STATUS_OVERLAY) != 0) { - vv->ov_suspend = vv->video_fh; - err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ - if (err != 0) { - dprintk(2, "suspending video failed\n"); - vv->ov_suspend = NULL; - } - } - - if (0 != av7110->current_input) { - dprintk(1, "switching to analog TV:\n"); - adswitch = 1; - source = SAA7146_HPS_SOURCE_PORT_B; - sync = SAA7146_HPS_SYNC_PORT_B; - memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); - - switch (av7110->current_input) { - case 1: - dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume - - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) - } - if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - case 2: - dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); - if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - case 3: - dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); - if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) - dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); - break; - default: - dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); - } - } else { - adswitch = 0; - source = SAA7146_HPS_SOURCE_PORT_A; - sync = SAA7146_HPS_SYNC_PORT_A; - memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); - dprintk(1, "switching DVB mode\n"); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume - - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - } - } - - /* hmm, this does not do anything!? */ - if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) - dprintk(1, "ADSwitch error\n"); - - saa7146_set_hps_source_and_sync(dev, source, sync); - - if (vv->ov_suspend != NULL) { - saa7146_start_preview(vv->ov_suspend); - vv->ov_suspend = NULL; - } - - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - u16 stereo_det; - s8 stereo; - - dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); - - if (!av7110->analog_tuner_flags || t->index != 0) - return -EINVAL; - - memset(t, 0, sizeof(*t)); - strscpy((char *)t->name, "Television", sizeof(t->name)); - - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ - t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ - /* FIXME: add the real signal strength here */ - t->signal = 0xffff; - t->afc = 0; - - /* FIXME: standard / stereo detection is still broken */ - msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); - dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); - msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); - dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); - stereo = (s8)(stereo_det >> 8); - if (stereo > 0x10) { - /* stereo */ - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - t->audmode = V4L2_TUNER_MODE_STEREO; - } else if (stereo < -0x10) { - /* bilingual */ - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - t->audmode = V4L2_TUNER_MODE_LANG1; - } else /* mono */ - t->rxsubchans = V4L2_TUNER_SUB_MONO; - - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - u16 fm_matrix, src; - dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); - fm_matrix = 0x3001; /* stereo */ - src = 0x0020; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); - fm_matrix = 0x3000; /* bilingual */ - src = 0x0020; - break; - case V4L2_TUNER_MODE_LANG1: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0000; - break; - case V4L2_TUNER_MODE_LANG2: - dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0010; - break; - default: /* case V4L2_TUNER_MODE_MONO: */ - dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); - fm_matrix = 0x3000; /* mono */ - src = 0x0030; - break; - } - msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); - msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); - msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); - msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - memset(f, 0, sizeof(*f)); - f->type = V4L2_TUNER_ANALOG_TV; - f->frequency = av7110->current_freq; - return 0; -} - -static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); - - if (!av7110->analog_tuner_flags || av7110->current_input != 1) - return -EINVAL; - - if (V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); - - /* tune in desired frequency */ - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) - ves1820_set_tv_freq(dev, f->frequency); - else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) - stv0297_set_tv_freq(dev, f->frequency); - av7110->current_freq = f->frequency; - - msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ - msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); - - if (av7110->analog_tuner_flags) { - if (i->index >= 4) - return -EINVAL; - } else { - if (i->index != 0) - return -EINVAL; - } - - memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); - - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - *input = av7110->current_input; - dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); - return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int input) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_INPUT: %d\n", input); - - if (!av7110->analog_tuner_flags) - return input ? -EINVAL : 0; - - if (input >= 4) - return -EINVAL; - - av7110->current_input = input; - return av7110_dvb_c_switch(fh); -} - -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); - if (a->index != 0) - return -EINVAL; - *a = msp3400_v4l2_audio; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); - if (a->index != 0) - return -EINVAL; - if (av7110->current_input >= 2) - return -EINVAL; - *a = msp3400_v4l2_audio; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); - if (av7110->current_input >= 2) - return -EINVAL; - return a->index ? -EINVAL : 0; -} - -static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, - struct v4l2_sliced_vbi_cap *cap) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); - if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) - return -EINVAL; - if (FW_VERSION(av7110->arm_app) >= 0x2623) { - cap->service_set = V4L2_SLICED_WSS_625; - cap->service_lines[0][23] = V4L2_SLICED_WSS_625; - } - return 0; -} - -static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_G_FMT:\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623) - return -EINVAL; - memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); - if (av7110->wssMode) { - f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; - f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); - } - return 0; -} - -static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; - - dprintk(2, "VIDIOC_S_FMT\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623) - return -EINVAL; - if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && - f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { - memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - /* WSS controlled by firmware */ - av7110->wssMode = 0; - av7110->wssData = 0; - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, - SetWSSConfig, 1, 0); - } else { - memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; - f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); - /* WSS controlled by userspace */ - av7110->wssMode = 1; - av7110->wssData = 0; - } - return 0; -} - -static int av7110_vbi_reset(struct file *file) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - - dprintk(2, "%s\n", __func__); - av7110->wssMode = 0; - av7110->wssData = 0; - if (FW_VERSION(av7110->arm_app) < 0x2623) - return 0; - else - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); -} - -static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) -{ - struct saa7146_fh *fh = file->private_data; - struct saa7146_dev *dev = fh->dev; - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - struct v4l2_sliced_vbi_data d; - int rc; - - dprintk(2, "%s\n", __func__); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) - return -EINVAL; - if (copy_from_user(&d, data, count)) - return -EFAULT; - if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) - return -EINVAL; - if (d.id) - av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; - else - av7110->wssData = 0x8000; - rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); - return (rc < 0) ? rc : count; -} - -/**************************************************************************** - * INITIALIZATION - ****************************************************************************/ - -static u8 saa7113_init_regs[] = { - 0x02, 0xd0, - 0x03, 0x23, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xe9, - 0x07, 0x0d, - 0x08, 0x98, - 0x09, 0x02, - 0x0a, 0x80, - 0x0b, 0x40, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x7c, - 0x10, 0x48, - 0x11, 0x0c, - 0x12, 0x8b, - 0x13, 0x1a, - 0x14, 0x00, - 0x15, 0x00, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1b, 0x00, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x00, - - 0x41, 0x77, - 0x42, 0x77, - 0x43, 0x77, - 0x44, 0x77, - 0x45, 0x77, - 0x46, 0x77, - 0x47, 0x77, - 0x48, 0x77, - 0x49, 0x77, - 0x4a, 0x77, - 0x4b, 0x77, - 0x4c, 0x77, - 0x4d, 0x77, - 0x4e, 0x77, - 0x4f, 0x77, - 0x50, 0x77, - 0x51, 0x77, - 0x52, 0x77, - 0x53, 0x77, - 0x54, 0x77, - 0x55, 0x77, - 0x56, 0x77, - 0x57, 0xff, - - 0xff -}; - - -static struct saa7146_ext_vv av7110_vv_data_st; -static struct saa7146_ext_vv av7110_vv_data_c; - -int av7110_init_analog_module(struct av7110 *av7110) -{ - u16 version1, version2; - - if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && - i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { - pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_MSP34x0; - } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && - i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { - pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", - av7110->dvb_adapter.num); - av7110->adac_type = DVB_ADAC_MSP34x5; - } else - return -ENODEV; - - msleep(100); // the probing above resets the msp... - msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); - msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); - dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", - av7110->dvb_adapter.num, version1, version2); - msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); - msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone - msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source - msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source - msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume - msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source - msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume - msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART - - if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { - pr_info("saa7113 not accessible\n"); - } else { - u8 *i = saa7113_init_regs; - - if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { - /* Fujitsu/Siemens DVB-Cable */ - av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { - /* Hauppauge/TT DVB-C premium */ - av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { - /* Hauppauge/TT DVB-C premium */ - av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; - } - - /* setup for DVB by default */ - if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { - if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) - dprintk(1, "setting band in demodulator failed\n"); - } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { - saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) - saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) - } - - /* init the saa7113 */ - while (*i != 0xff) { - if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { - dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); - break; - } - i += 2; - } - /* setup msp for analog sound: B/G Dual-FM */ - msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 - msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG - msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz - msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI - msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz - msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI - msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 - } - - memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); - /* set dd1 stream a & b */ - saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); - saa7146_write(av7110->dev, DD1_INIT, 0x03000700); - saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - - return 0; -} - -int av7110_init_v4l(struct av7110 *av7110) -{ - struct saa7146_dev* dev = av7110->dev; - struct saa7146_ext_vv *vv_data; - int ret; - - /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ - if (av7110->analog_tuner_flags) - vv_data = &av7110_vv_data_c; - else - vv_data = &av7110_vv_data_st; - ret = saa7146_vv_init(dev, vv_data); - - if (ret) { - ERR("cannot init capture device. skipping\n"); - return -ENODEV; - } - vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; - vv_data->vid_ops.vidioc_g_input = vidioc_g_input; - vv_data->vid_ops.vidioc_s_input = vidioc_s_input; - vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; - vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; - vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; - vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; - - vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; - vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; - vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; - vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; - - if (FW_VERSION(av7110->arm_app) < 0x2623) - vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; - - if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_VIDEO)) { - ERR("cannot register capture device. skipping\n"); - saa7146_vv_release(dev); - return -ENODEV; - } - if (FW_VERSION(av7110->arm_app) >= 0x2623) { - if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) - ERR("cannot register vbi v4l2 device. skipping\n"); - } - return 0; -} - -int av7110_exit_v4l(struct av7110 *av7110) -{ - struct saa7146_dev* dev = av7110->dev; - - saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); - saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); - - saa7146_vv_release(dev); - - return 0; -} - - - -/* FIXME: these values are experimental values that look better than the - values from the latest "official" driver -- at least for me... (MiHu) */ -static struct saa7146_standard standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x15, .v_field = 288, - .h_offset = 0x48, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static struct saa7146_standard analog_standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x1b, .v_field = 288, - .h_offset = 0x08, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static struct saa7146_standard dvb_standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL_BG, - .v_offset = 0x14, .v_field = 288, - .h_offset = 0x48, .h_pixels = 708, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x10, .v_field = 244, - .h_offset = 0x40, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - } -}; - -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) -{ - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; - - if (std->id & V4L2_STD_PAL) { - av7110->vidmode = AV7110_VIDEO_MODE_PAL; - av7110_set_vidmode(av7110, av7110->vidmode); - } - else if (std->id & V4L2_STD_NTSC) { - av7110->vidmode = AV7110_VIDEO_MODE_NTSC; - av7110_set_vidmode(av7110, av7110->vidmode); - } - else - return -1; - - return 0; -} - - -static struct saa7146_ext_vv av7110_vv_data_st = { - .inputs = 1, - .audios = 1, - .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, - .flags = 0, - - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), - .std_callback = &std_callback, - - .vbi_fops.open = av7110_vbi_reset, - .vbi_fops.release = av7110_vbi_reset, - .vbi_fops.write = av7110_vbi_write, -}; - -static struct saa7146_ext_vv av7110_vv_data_c = { - .inputs = 1, - .audios = 1, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, - .flags = SAA7146_USE_PORT_B_FOR_VBI, - - .stds = &standard[0], - .num_stds = ARRAY_SIZE(standard), - .std_callback = &std_callback, - - .vbi_fops.open = av7110_vbi_reset, - .vbi_fops.release = av7110_vbi_reset, - .vbi_fops.write = av7110_vbi_write, -}; - diff --git a/drivers/staging/media/av7110/budget-patch.c b/drivers/staging/media/av7110/budget-patch.c deleted file mode 100644 index d173c8ade6a7..000000000000 --- a/drivers/staging/media/av7110/budget-patch.c +++ /dev/null @@ -1,665 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget-patch.c: driver for Budget Patch, - * hardware modification of DVB-S cards enabling full TS - * - * Written by Emard - * - * Original idea by Roberto Deza - * - * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic - * and Metzlerbros - * - * the project's page is at https://linuxtv.org - */ - -#include "av7110.h" -#include "av7110_hw.h" -#include "budget.h" -#include "stv0299.h" -#include "ves1x93.h" -#include "tda8083.h" - -#include "bsru6.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define budget_patch budget - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); -//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), -// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), - { - .vendor = 0, - } -}; - -/* those lines are for budget-patch to be tried -** on a true budget card and observe the -** behaviour of VSYNC generated by rps1. -** this code was shamelessly copy/pasted from budget.c -*/ -static void gpio_Set22K (struct budget *budget, int state) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); -} - -/* Diseqc functions only for TT Budget card */ -/* taken from the Skyvision DVB driver by - Ralph Metzler */ - -static void DiseqcSendBit (struct budget *budget, int data) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(data ? 500 : 1000); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - udelay(data ? 1000 : 500); -} - -static void DiseqcSendByte (struct budget *budget, int data) -{ - int i, par=1, d; - - dprintk(2, "budget: %p\n", budget); - - for (i=7; i>=0; i--) { - d = (data>>i)&1; - par ^= d; - DiseqcSendBit(budget, d); - } - - DiseqcSendBit(budget, par); -} - -static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) -{ - struct saa7146_dev *dev=budget->dev; - int i; - - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - mdelay(16); - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - gpio_Set22K (budget, 1); - break; - - case SEC_TONE_OFF: - gpio_Set22K (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, 0, NULL, minicmd); - - return 0; -} - -static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) -{ - int i; - - dprintk(2, "budget: %p\n", budget); - - for (i = 2; i < length; i++) - { - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); - msleep(5); - } - if (length) - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); - else - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); - msleep(5); - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); - msleep(5); - return 0; -} - -static void av7110_set22k(struct budget_patch *budget, int state) -{ - u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; - - dprintk(2, "budget: %p\n", budget); - budget_av7110_send_fw_cmd(budget, buf, 2); -} - -static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) -{ - int i; - u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(2, "budget: %p\n", budget); - - if (len>10) - len=10; - - buf[1] = len+2; - buf[2] = len; - - if (burst != -1) - buf[3]=burst ? 0x01 : 0x00; - else - buf[3]=0xffff; - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - av7110_set22k (budget, 1); - break; - - case SEC_TONE_OFF: - av7110_set22k (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_patch_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, 0, NULL, minicmd); - - return 0; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (p->frequency + 479500) / 125; - - if (p->frequency > 2000000) - pwr = 3; - else if (p->frequency > 1800000) - pwr = 2; - else if (p->frequency > 1600000) - pwr = 1; - else if (p->frequency > 1200000) - pwr = 0; - else if (p->frequency >= 1100000) - pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = { - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = p->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - -static void frontend_init(struct budget_patch* budget) -{ - switch(budget->dev->pci->subsystem_device) { - case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X - case 0x1013: // SATELCO Multimedia PCI - - // try the ALPS BSRV2 first of all - budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; - break; - } - - // try the ALPS BSRU6 now - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - - // Try the grundig 29504-451 - budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - break; - } - - if (budget->dvb_frontend == NULL) { - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget->dev->pci->vendor, - budget->dev->pci->device, - budget->dev->pci->subsystem_vendor, - budget->dev->pci->subsystem_device); - } else { - if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { - printk("budget-av: Frontend registration failed!\n"); - dvb_frontend_detach(budget->dvb_frontend); - budget->dvb_frontend = NULL; - } - } -} - -/* written by Emard */ -static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct budget_patch *budget; - int err; - int count = 0; - int detected = 0; - -#define PATCH_RESET 0 -#define RPS_IRQ 0 -#define HPS_SETUP 0 -#if PATCH_RESET - saa7146_write(dev, MC1, MASK_31); - msleep(40); -#endif -#if HPS_SETUP - // initialize registers. Better to have it like this - // than leaving something unconfigured - saa7146_write(dev, DD1_STREAM_B, 0); - // port B VSYNC at rising edge - saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! - saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI - - // debi config - // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); - - // zero all HPS registers - saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 - saa7146_write(dev, HPS_H_SCALE, 0); // r6c - saa7146_write(dev, BCS_CTRL, 0); // r70 - saa7146_write(dev, HPS_V_SCALE, 0); // r60 - saa7146_write(dev, HPS_V_GAIN, 0); // r64 - saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 - saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 - // Set HPS prescaler for port B input - saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); - saa7146_write(dev, MC2, - 0 * (MASK_08 | MASK_24) | // BRS control - 0 * (MASK_09 | MASK_25) | // a - 0 * (MASK_10 | MASK_26) | // b - 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 - 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 - 0 * (MASK_01 | MASK_15) // DEBI - ); -#endif - // Disable RPS1 and RPS0 - saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); - // RPS1 timeout disable - saa7146_write(dev, RPS_TOV1, 0); - - // code for autodetection - // will wait for VBI_B event (vertical blank at port B) - // and will reset GPIO3 after VBI_B is detected. - // (GPIO3 should be raised high by CPU to - // test if GPIO3 will generate vertical blank signal - // in budget patch GPIO3 is connected to VSYNC_B - count = 0; -#if 0 - WRITE_RPS1(CMD_UPLOAD | - MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); -#endif - WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt to increment counter - WRITE_RPS1(CMD_INTERRUPT); - // at least a NOP is neede between two interrupts - WRITE_RPS1(CMD_NOP); - // interrupt again - WRITE_RPS1(CMD_INTERRUPT); -#endif - WRITE_RPS1(CMD_STOP); - -#if RPS_IRQ - // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - // set event counter 1 threshold to maximum allowed value (rEC p55) - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - // Enable RPS1, (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); - - - mdelay(50); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - mdelay(150); - - - if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) - detected = 1; - -#if RPS_IRQ - printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); -#endif - // Disable RPS1 - saa7146_write(dev, MC1, ( MASK_29 )); - - if(detected == 0) - printk("budget-patch not detected or saa7146 in non-default state.\n" - "try enabling resetting of 7146 with MASK_31 in MC1 register\n"); - - else - printk("BUDGET-PATCH DETECTED.\n"); - - -/* OLD (Original design by Roberto Deza): -** This code will setup the SAA7146_RPS1 to generate a square -** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of -** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; -** then, this GPIO3 output which is connected to the D1B_VSYNC -** input, will trigger the acquisition of the alternate field -** and so on. -** Currently, the TT_budget / WinTV_Nova cards have two ICs -** (74HCT4040, LVC74) for the generation of this VSYNC signal, -** which seems that can be done perfectly without this :-)). -*/ - -/* New design (By Emard) -** this rps1 code will copy internal HS event to GPIO3 pin. -** GPIO3 is in budget-patch hardware connected to port B VSYNC - -** HS is an internal event of 7146, accessible with RPS -** and temporarily raised high every n lines -** (n in defined in the RPS_THRESH1 counter threshold) -** I think HS is raised high on the beginning of the n-th line -** and remains high until this n-th line that triggered -** it is completely received. When the reception of n-th line -** ends, HS is lowered. - -** To transmit data over DMA, 7146 needs changing state at -** port B VSYNC pin. Any changing of port B VSYNC will -** cause some DMA data transfer, with more or less packets loss. -** It depends on the phase and frequency of VSYNC and -** the way of 7146 is instructed to trigger on port B (defined -** in DD1_INIT register, 3rd nibble from the right valid -** numbers are 0-7, see datasheet) -** -** The correct triggering can minimize packet loss, -** dvbtraffic should give this stable bandwidths: -** 22k transponder = 33814 kbit/s -** 27.5k transponder = 38045 kbit/s -** by experiment it is found that the best results -** (stable bandwidths and almost no packet loss) -** are obtained using DD1_INIT triggering number 2 -** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) -** and a VSYNC phase that occurs in the middle of DMA transfer -** (about byte 188*512=96256 in the DMA window). -** -** Phase of HS is still not clear to me how to control, -** It just happens to be so. It can be seen if one enables -** RPS_IRQ and print Event Counter 1 in vpeirq(). Every -** time RPS_INTERRUPT is called, the Event Counter 1 will -** increment. That's how the 7146 is programmed to do event -** counting in this budget-patch.c -** I *think* HPS setting has something to do with the phase -** of HS but I can't be 100% sure in that. - -** hardware debug note: a working budget card (including budget patch) -** with vpeirq() interrupt setup in mode "0x90" (every 64K) will -** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes -** and that means 3*25=75 Hz of interrupt frequency, as seen by -** watch cat /proc/interrupts -** -** If this frequency is 3x lower (and data received in the DMA -** buffer don't start with 0x47, but in the middle of packets, -** whose lengths appear to be like 188 292 188 104 etc. -** this means VSYNC line is not connected in the hardware. -** (check soldering pcb and pins) -** The same behaviour of missing VSYNC can be duplicated on budget -** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. -*/ - - // Setup RPS1 "program" (p35) - count = 0; - - - // Wait Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | EVT_HS); - // Set GPIO3=1 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Wait reset Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); - // Set GPIO3=0 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Jump to begin of RPS program (p37) - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - - if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) - return -ENOMEM; - - dprintk(2, "budget: %p\n", budget); - - err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); - if (err) { - kfree(budget); - return err; - } - - // Set Source Line Counter Threshold, using BRS (rCC p43) - // It generates HS event every TS_HEIGHT lines - // this is related to TS_WIDTH set in register - // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE - // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 - //,then RPS_THRESH1 - // should be set to trigger every TS_HEIGHT (512) lines. - // - saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); - - // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); - // Enable RPS1 (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - - dev->ext_priv = budget; - - budget->dvb_adapter.priv = budget; - frontend_init(budget); - - ttpci_budget_init_hooks(budget); - - return 0; -} - -static int budget_patch_detach (struct saa7146_dev* dev) -{ - struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; - int err; - - if (budget->dvb_frontend) { - dvb_unregister_frontend(budget->dvb_frontend); - dvb_frontend_detach(budget->dvb_frontend); - } - err = ttpci_budget_deinit (budget); - - kfree (budget); - - return err; -} - -static int __init budget_patch_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_patch_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -static struct saa7146_extension budget_extension = { - .name = "budget_patch dvb", - .flags = 0, - - .module = THIS_MODULE, - .pci_tbl = pci_tbl, - .attach = budget_patch_attach, - .detach = budget_patch_detach, - - .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, -}; - -module_init(budget_patch_init); -module_exit(budget_patch_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); -MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 based so-called Budget Patch cards"); diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c deleted file mode 100644 index 8c2eca5dcdc9..000000000000 --- a/drivers/staging/media/av7110/dvb_filter.c +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include "dvb_filter.h" - -static u32 freq[4] = {480, 441, 320, 0}; - -static unsigned int ac3_bitrates[32] = - {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, - 0,0,0,0,0,0,0,0,0,0,0,0,0}; - -static u32 ac3_frames[3][32] = - {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, - 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, - 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, - 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; - -int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) -{ - u8 *headr; - int found = 0; - int c = 0; - u8 frame = 0; - int fr = 0; - - while ( !found && c < count){ - u8 *b = mbuf+c; - - if ( b[0] == 0x0b && b[1] == 0x77 ) - found = 1; - else { - c++; - } - } - - if (!found) return -1; - if (pr) - printk(KERN_DEBUG "Audiostream: AC3"); - - ai->off = c; - if (c+5 >= count) return -1; - - ai->layer = 0; // 0 for AC3 - headr = mbuf+c+2; - - frame = (headr[2]&0x3f); - ai->bit_rate = ac3_bitrates[frame >> 1]*1000; - - if (pr) - printk(KERN_CONT " BRate: %d kb/s", (int) ai->bit_rate/1000); - - ai->frequency = (headr[2] & 0xc0 ) >> 6; - fr = (headr[2] & 0xc0 ) >> 6; - ai->frequency = freq[fr]*100; - if (pr) - printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); - - ai->framesize = ac3_frames[fr][frame >> 1]; - if ((frame & 1) && (fr == 1)) ai->framesize++; - ai->framesize = ai->framesize << 1; - if (pr) - printk(KERN_DEBUG " Framesize %d\n", (int) ai->framesize); - - return 0; -} - -void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, - dvb_filter_pes2ts_cb_t *cb, void *priv) -{ - unsigned char *buf=p2ts->buf; - - buf[0]=0x47; - buf[1]=(pid>>8); - buf[2]=pid&0xff; - p2ts->cc=0; - p2ts->cb=cb; - p2ts->priv=priv; -} - -int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, - int len, int payload_start) -{ - unsigned char *buf=p2ts->buf; - int ret=0, rest; - - //len=6+((pes[4]<<8)|pes[5]); - - if (payload_start) - buf[1]|=0x40; - else - buf[1]&=~0x40; - while (len>=184) { - buf[3]=0x10|((p2ts->cc++)&0x0f); - memcpy(buf+4, pes, 184); - if ((ret=p2ts->cb(p2ts->priv, buf))) - return ret; - len-=184; pes+=184; - buf[1]&=~0x40; - } - if (!len) - return 0; - buf[3]=0x30|((p2ts->cc++)&0x0f); - rest=183-len; - if (rest) { - buf[5]=0x00; - if (rest-1) - memset(buf+6, 0xff, rest-1); - } - buf[4]=rest; - memcpy(buf+5+rest, pes, len); - return p2ts->cb(p2ts->priv, buf); -} diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/av7110/dvb_filter.h deleted file mode 100644 index 67a3c6333bca..000000000000 --- a/drivers/staging/media/av7110/dvb_filter.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * dvb_filter.h - * - * Copyright (C) 2003 Convergence GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _DVB_FILTER_H_ -#define _DVB_FILTER_H_ - -#include - -#include - -typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *); - -struct dvb_filter_pes2ts { - unsigned char buf[188]; - unsigned char cc; - dvb_filter_pes2ts_cb_t *cb; - void *priv; -}; - -void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, - dvb_filter_pes2ts_cb_t *cb, void *priv); - -int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, - int len, int payload_start); - - -#define PROG_STREAM_MAP 0xBC -#define PRIVATE_STREAM1 0xBD -#define PADDING_STREAM 0xBE -#define PRIVATE_STREAM2 0xBF -#define AUDIO_STREAM_S 0xC0 -#define AUDIO_STREAM_E 0xDF -#define VIDEO_STREAM_S 0xE0 -#define VIDEO_STREAM_E 0xEF -#define ECM_STREAM 0xF0 -#define EMM_STREAM 0xF1 -#define DSM_CC_STREAM 0xF2 -#define ISO13522_STREAM 0xF3 -#define PROG_STREAM_DIR 0xFF - -#define DVB_PICTURE_START 0x00 -#define DVB_USER_START 0xb2 -#define DVB_SEQUENCE_HEADER 0xb3 -#define DVB_SEQUENCE_ERROR 0xb4 -#define DVB_EXTENSION_START 0xb5 -#define DVB_SEQUENCE_END 0xb7 -#define DVB_GOP_START 0xb8 -#define DVB_EXCEPT_SLICE 0xb0 - -#define SEQUENCE_EXTENSION 0x01 -#define SEQUENCE_DISPLAY_EXTENSION 0x02 -#define PICTURE_CODING_EXTENSION 0x08 -#define QUANT_MATRIX_EXTENSION 0x03 -#define PICTURE_DISPLAY_EXTENSION 0x07 - -#define I_FRAME 0x01 -#define B_FRAME 0x02 -#define P_FRAME 0x03 - -/* Initialize sequence_data */ -#define INIT_HORIZONTAL_SIZE 720 -#define INIT_VERTICAL_SIZE 576 -#define INIT_ASPECT_RATIO 0x02 -#define INIT_FRAME_RATE 0x03 -#define INIT_DISP_HORIZONTAL_SIZE 540 -#define INIT_DISP_VERTICAL_SIZE 576 - - -//flags2 -#define PTS_DTS_FLAGS 0xC0 -#define ESCR_FLAG 0x20 -#define ES_RATE_FLAG 0x10 -#define DSM_TRICK_FLAG 0x08 -#define ADD_CPY_FLAG 0x04 -#define PES_CRC_FLAG 0x02 -#define PES_EXT_FLAG 0x01 - -//pts_dts flags -#define PTS_ONLY 0x80 -#define PTS_DTS 0xC0 - -#define TS_SIZE 188 -#define TRANS_ERROR 0x80 -#define PAY_START 0x40 -#define TRANS_PRIO 0x20 -#define PID_MASK_HI 0x1F -//flags -#define TRANS_SCRMBL1 0x80 -#define TRANS_SCRMBL2 0x40 -#define ADAPT_FIELD 0x20 -#define PAYLOAD 0x10 -#define COUNT_MASK 0x0F - -// adaptation flags -#define DISCON_IND 0x80 -#define RAND_ACC_IND 0x40 -#define ES_PRI_IND 0x20 -#define PCR_FLAG 0x10 -#define OPCR_FLAG 0x08 -#define SPLICE_FLAG 0x04 -#define TRANS_PRIV 0x02 -#define ADAP_EXT_FLAG 0x01 - -// adaptation extension flags -#define LTW_FLAG 0x80 -#define PIECE_RATE 0x40 -#define SEAM_SPLICE 0x20 - - -#define MAX_PLENGTH 0xFFFF -#define MMAX_PLENGTH (256*MAX_PLENGTH) - -#ifndef IPACKS -#define IPACKS 2048 -#endif - -struct ipack { - int size; - int found; - u8 *buf; - u8 cid; - u32 plength; - u8 plen[2]; - u8 flag1; - u8 flag2; - u8 hlength; - u8 pts[5]; - u16 *pid; - int mpeg; - u8 check; - int which; - int done; - void *data; - void (*func)(u8 *buf, int size, void *priv); - int count; - int repack_subids; -}; - -struct dvb_video_info { - u32 horizontal_size; - u32 vertical_size; - u32 aspect_ratio; - u32 framerate; - u32 video_format; - u32 bit_rate; - u32 comp_bit_rate; - u32 vbv_buffer_size; - s16 vbv_delay; - u32 CSPF; - u32 off; -}; - -#define OFF_SIZE 4 -#define FIRST_FIELD 0 -#define SECOND_FIELD 1 -#define VIDEO_FRAME_PICTURE 0x03 - -struct mpg_picture { - int channel; - struct dvb_video_info vinfo; - u32 *sequence_gop_header; - u32 *picture_header; - s32 time_code; - int low_delay; - int closed_gop; - int broken_link; - int sequence_header_flag; - int gop_flag; - int sequence_end_flag; - - u8 profile_and_level; - s32 picture_coding_parameter; - u32 matrix[32]; - s8 matrix_change_flag; - - u8 picture_header_parameter; - /* bit 0 - 2: bwd f code - bit 3 : fpb vector - bit 4 - 6: fwd f code - bit 7 : fpf vector */ - - int mpeg1_flag; - int progressive_sequence; - int sequence_display_extension_flag; - u32 sequence_header_data; - s16 last_frame_centre_horizontal_offset; - s16 last_frame_centre_vertical_offset; - - u32 pts[2]; /* [0] 1st field, [1] 2nd field */ - int top_field_first; - int repeat_first_field; - int progressive_frame; - int bank; - int forward_bank; - int backward_bank; - int compress; - s16 frame_centre_horizontal_offset[OFF_SIZE]; - /* [0-2] 1st field, [3] 2nd field */ - s16 frame_centre_vertical_offset[OFF_SIZE]; - /* [0-2] 1st field, [3] 2nd field */ - s16 temporal_reference[2]; - /* [0] 1st field, [1] 2nd field */ - - s8 picture_coding_type[2]; - /* [0] 1st field, [1] 2nd field */ - s8 picture_structure[2]; - /* [0] 1st field, [1] 2nd field */ - s8 picture_display_extension_flag[2]; - /* [0] 1st field, [1] 2nd field */ - /* picture_display_extenion() 0:no 1:exit*/ - s8 pts_flag[2]; - /* [0] 1st field, [1] 2nd field */ -}; - -struct dvb_audio_info { - int layer; - u32 bit_rate; - u32 frequency; - u32 mode; - u32 mode_extension ; - u32 emphasis; - u32 framesize; - u32 off; -}; - -int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr); - - -#endif diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c deleted file mode 100644 index 9767159aeb9b..000000000000 --- a/drivers/staging/media/av7110/sp8870.c +++ /dev/null @@ -1,609 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - Driver for Spase SP8870 demodulator - - Copyright (C) 1999 Juergen Peitz - - -*/ -/* - * This driver needs external firmware. Please use the command - * "/scripts/get_dvb_firmware alps_tdlb7" to - * download/extract it, and then copy it to /usr/lib/hotplug/firmware - * or /lib/firmware (depending on configuration of firmware hotplug). - */ -#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include "sp8870.h" - - -struct sp8870_state { - - struct i2c_adapter* i2c; - - const struct sp8870_config* config; - - struct dvb_frontend frontend; - - /* demodulator private data */ - u8 initialised:1; -}; - -static int debug; -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "sp8870: " args); \ - } while (0) - -/* firmware size for sp8870 */ -#define SP8870_FIRMWARE_SIZE 16382 - -/* starting point for firmware in file 'Sc_main.mc' */ -#define SP8870_FIRMWARE_OFFSET 0x0A - -static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) -{ - u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; - int err; - - if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } - - return 0; -} - -static int sp8870_readreg (struct sp8870_state* state, u16 reg) -{ - int ret; - u8 b0 [] = { reg >> 8 , reg & 0xff }; - u8 b1 [] = { 0, 0 }; - struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, - { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; - - ret = i2c_transfer (state->i2c, msg, 2); - - if (ret != 2) { - dprintk("%s: readreg error (ret == %i)\n", __func__, ret); - return -1; - } - - return (b1[0] << 8 | b1[1]); -} - -static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) -{ - struct i2c_msg msg; - const char *fw_buf = fw->data; - int fw_pos; - u8 tx_buf[255]; - int tx_len; - int err = 0; - - dprintk ("%s: ...\n", __func__); - - if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) - return -EINVAL; - - // system controller stop - sp8870_writereg(state, 0x0F00, 0x0000); - - // instruction RAM register hiword - sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); - - // instruction RAM MWR - sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); - - // do firmware upload - fw_pos = SP8870_FIRMWARE_OFFSET; - while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ - tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; - // write register 0xCF0A - tx_buf[0] = 0xCF; - tx_buf[1] = 0x0A; - memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.buf = tx_buf; - msg.len = tx_len + 2; - if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk("%s: firmware upload failed!\n", __func__); - printk ("%s: i2c error (err == %i)\n", __func__, err); - return err; - } - fw_pos += tx_len; - } - - dprintk ("%s: done!\n", __func__); - return 0; -}; - -static void sp8870_microcontroller_stop (struct sp8870_state* state) -{ - sp8870_writereg(state, 0x0F08, 0x000); - sp8870_writereg(state, 0x0F09, 0x000); - - // microcontroller STOP - sp8870_writereg(state, 0x0F00, 0x000); -} - -static void sp8870_microcontroller_start (struct sp8870_state* state) -{ - sp8870_writereg(state, 0x0F08, 0x000); - sp8870_writereg(state, 0x0F09, 0x000); - - // microcontroller START - sp8870_writereg(state, 0x0F00, 0x001); - // not documented but if we don't read 0x0D01 out here - // we don't get a correct data valid signal - sp8870_readreg(state, 0x0D01); -} - -static int sp8870_read_data_valid_signal(struct sp8870_state* state) -{ - return (sp8870_readreg(state, 0x0D02) > 0); -} - -static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) -{ - int known_parameters = 1; - - *reg0xc05 = 0x000; - - switch (p->modulation) { - case QPSK: - break; - case QAM_16: - *reg0xc05 |= (1 << 10); - break; - case QAM_64: - *reg0xc05 |= (2 << 10); - break; - case QAM_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - } - - switch (p->hierarchy) { - case HIERARCHY_NONE: - break; - case HIERARCHY_1: - *reg0xc05 |= (1 << 7); - break; - case HIERARCHY_2: - *reg0xc05 |= (2 << 7); - break; - case HIERARCHY_4: - *reg0xc05 |= (3 << 7); - break; - case HIERARCHY_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - } - - switch (p->code_rate_HP) { - case FEC_1_2: - break; - case FEC_2_3: - *reg0xc05 |= (1 << 3); - break; - case FEC_3_4: - *reg0xc05 |= (2 << 3); - break; - case FEC_5_6: - *reg0xc05 |= (3 << 3); - break; - case FEC_7_8: - *reg0xc05 |= (4 << 3); - break; - case FEC_AUTO: - known_parameters = 0; - break; - default: - return -EINVAL; - } - - if (known_parameters) - *reg0xc05 |= (2 << 1); /* use specified parameters */ - else - *reg0xc05 |= (1 << 1); /* enable autoprobing */ - - return 0; -} - -static int sp8870_wake_up(struct sp8870_state* state) -{ - // enable TS output and interface pins - return sp8870_writereg(state, 0xC18, 0x00D); -} - -static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct sp8870_state* state = fe->demodulator_priv; - int err; - u16 reg0xc05; - - if ((err = configure_reg0xc05(p, ®0xc05))) - return err; - - // system controller stop - sp8870_microcontroller_stop(state); - - // set tuner parameters - if (fe->ops.tuner_ops.set_params) { - fe->ops.tuner_ops.set_params(fe); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - } - - // sample rate correction bit [23..17] - sp8870_writereg(state, 0x0319, 0x000A); - - // sample rate correction bit [16..0] - sp8870_writereg(state, 0x031A, 0x0AAB); - - // integer carrier offset - sp8870_writereg(state, 0x0309, 0x0400); - - // fractional carrier offset - sp8870_writereg(state, 0x030A, 0x0000); - - // filter for 6/7/8 Mhz channel - if (p->bandwidth_hz == 6000000) - sp8870_writereg(state, 0x0311, 0x0002); - else if (p->bandwidth_hz == 7000000) - sp8870_writereg(state, 0x0311, 0x0001); - else - sp8870_writereg(state, 0x0311, 0x0000); - - // scan order: 2k first = 0x0000, 8k first = 0x0001 - if (p->transmission_mode == TRANSMISSION_MODE_2K) - sp8870_writereg(state, 0x0338, 0x0000); - else - sp8870_writereg(state, 0x0338, 0x0001); - - sp8870_writereg(state, 0xc05, reg0xc05); - - // read status reg in order to clear pending irqs - err = sp8870_readreg(state, 0x200); - if (err < 0) - return err; - - // system controller start - sp8870_microcontroller_start(state); - - return 0; -} - -static int sp8870_init (struct dvb_frontend* fe) -{ - struct sp8870_state* state = fe->demodulator_priv; - const struct firmware *fw = NULL; - - sp8870_wake_up(state); - if (state->initialised) return 0; - state->initialised = 1; - - dprintk ("%s\n", __func__); - - - /* request the firmware, this will block until someone uploads it */ - printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); - if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { - printk("sp8870: no firmware upload (timeout or file not found?)\n"); - return -EIO; - } - - if (sp8870_firmware_upload(state, fw)) { - printk("sp8870: writing firmware to device failed\n"); - release_firmware(fw); - return -EIO; - } - release_firmware(fw); - printk("sp8870: firmware upload complete\n"); - - /* enable TS output and interface pins */ - sp8870_writereg(state, 0xc18, 0x00d); - - // system controller stop - sp8870_microcontroller_stop(state); - - // ADC mode - sp8870_writereg(state, 0x0301, 0x0003); - - // Reed Solomon parity bytes passed to output - sp8870_writereg(state, 0x0C13, 0x0001); - - // MPEG clock is suppressed if no valid data - sp8870_writereg(state, 0x0C14, 0x0001); - - /* bit 0x010: enable data valid signal */ - sp8870_writereg(state, 0x0D00, 0x010); - sp8870_writereg(state, 0x0D01, 0x000); - - return 0; -} - -static int sp8870_read_status(struct dvb_frontend *fe, - enum fe_status *fe_status) -{ - struct sp8870_state* state = fe->demodulator_priv; - int status; - int signal; - - *fe_status = 0; - - status = sp8870_readreg (state, 0x0200); - if (status < 0) - return -EIO; - - signal = sp8870_readreg (state, 0x0303); - if (signal < 0) - return -EIO; - - if (signal > 0x0F) - *fe_status |= FE_HAS_SIGNAL; - if (status & 0x08) - *fe_status |= FE_HAS_SYNC; - if (status & 0x04) - *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; - - return 0; -} - -static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) -{ - struct sp8870_state* state = fe->demodulator_priv; - int ret; - u32 tmp; - - *ber = 0; - - ret = sp8870_readreg(state, 0xC08); - if (ret < 0) - return -EIO; - - tmp = ret & 0x3F; - - ret = sp8870_readreg(state, 0xC07); - if (ret < 0) - return -EIO; - - tmp = ret << 6; - if (tmp >= 0x3FFF0) - tmp = ~0; - - *ber = tmp; - - return 0; -} - -static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) -{ - struct sp8870_state* state = fe->demodulator_priv; - int ret; - u16 tmp; - - *signal = 0; - - ret = sp8870_readreg (state, 0x306); - if (ret < 0) - return -EIO; - - tmp = ret << 8; - - ret = sp8870_readreg (state, 0x303); - if (ret < 0) - return -EIO; - - tmp |= ret; - - if (tmp) - *signal = 0xFFFF - tmp; - - return 0; -} - -static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) -{ - struct sp8870_state* state = fe->demodulator_priv; - int ret; - - *ublocks = 0; - - ret = sp8870_readreg(state, 0xC0C); - if (ret < 0) - return -EIO; - - if (ret == 0xFFFF) - ret = ~0; - - *ublocks = ret; - - return 0; -} - -/* number of trials to recover from lockup */ -#define MAXTRIALS 5 -/* maximum checks for data valid signal */ -#define MAXCHECKS 100 - -/* only for debugging: counter for detected lockups */ -static int lockups; -/* only for debugging: counter for channel switches */ -static int switches; - -static int sp8870_set_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct sp8870_state* state = fe->demodulator_priv; - - /* - The firmware of the sp8870 sometimes locks up after setting frontend parameters. - We try to detect this by checking the data valid signal. - If it is not set after MAXCHECKS we try to recover the lockup by setting - the frontend parameters again. - */ - - int err = 0; - int valid = 0; - int trials = 0; - int check_count = 0; - - dprintk("%s: frequency = %i\n", __func__, p->frequency); - - for (trials = 1; trials <= MAXTRIALS; trials++) { - - err = sp8870_set_frontend_parameters(fe); - if (err) - return err; - - for (check_count = 0; check_count < MAXCHECKS; check_count++) { -// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); - valid = sp8870_read_data_valid_signal(state); - if (valid) { - dprintk("%s: delay = %i usec\n", - __func__, check_count * 10); - break; - } - udelay(10); - } - if (valid) - break; - } - - if (!valid) { - printk("%s: firmware crash!!!!!!\n", __func__); - return -EIO; - } - - if (debug) { - if (valid) { - if (trials > 1) { - printk("%s: firmware lockup!!!\n", __func__); - printk("%s: recovered after %i trial(s))\n", __func__, trials - 1); - lockups++; - } - } - switches++; - printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups); - } - - return 0; -} - -static int sp8870_sleep(struct dvb_frontend* fe) -{ - struct sp8870_state* state = fe->demodulator_priv; - - // tristate TS output and disable interface pins - return sp8870_writereg(state, 0xC18, 0x000); -} - -static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) -{ - fesettings->min_delay_ms = 350; - fesettings->step_size = 0; - fesettings->max_drift = 0; - return 0; -} - -static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) -{ - struct sp8870_state* state = fe->demodulator_priv; - - if (enable) { - return sp8870_writereg(state, 0x206, 0x001); - } else { - return sp8870_writereg(state, 0x206, 0x000); - } -} - -static void sp8870_release(struct dvb_frontend* fe) -{ - struct sp8870_state* state = fe->demodulator_priv; - kfree(state); -} - -static const struct dvb_frontend_ops sp8870_ops; - -struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c) -{ - struct sp8870_state* state = NULL; - - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); - if (state == NULL) goto error; - - /* setup the state */ - state->config = config; - state->i2c = i2c; - state->initialised = 0; - - /* check if the demod is there */ - if (sp8870_readreg(state, 0x0200) < 0) goto error; - - /* create dvb_frontend */ - memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); - state->frontend.demodulator_priv = state; - return &state->frontend; - -error: - kfree(state); - return NULL; -} - -static const struct dvb_frontend_ops sp8870_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "Spase SP8870 DVB-T", - .frequency_min_hz = 470 * MHz, - .frequency_max_hz = 860 * MHz, - .frequency_stepsize_hz = 166666, - .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | - FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER - }, - - .release = sp8870_release, - - .init = sp8870_init, - .sleep = sp8870_sleep, - .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, - - .set_frontend = sp8870_set_frontend, - .get_tune_settings = sp8870_get_tune_settings, - - .read_status = sp8870_read_status, - .read_ber = sp8870_read_ber, - .read_signal_strength = sp8870_read_signal_strength, - .read_ucblocks = sp8870_read_uncorrected_blocks, -}; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - -MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); -MODULE_AUTHOR("Juergen Peitz"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(sp8870_attach); diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/av7110/sp8870.h deleted file mode 100644 index 5eacf39f425e..000000000000 --- a/drivers/staging/media/av7110/sp8870.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - Driver for Spase SP8870 demodulator - - Copyright (C) 1999 Juergen Peitz - - -*/ - -#ifndef SP8870_H -#define SP8870_H - -#include -#include - -struct sp8870_config -{ - /* the demodulator's i2c address */ - u8 demod_address; - - /* request firmware for device */ - int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); -}; - -#if IS_REACHABLE(CONFIG_DVB_SP8870) -extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c); -#else -static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif // CONFIG_DVB_SP8870 - -#endif // SP8870_H diff --git a/drivers/staging/media/av7110/video-clear-buffer.rst b/drivers/staging/media/av7110/video-clear-buffer.rst deleted file mode 100644 index a7730559bbb2..000000000000 --- a/drivers/staging/media/av7110/video-clear-buffer.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_CLEAR_BUFFER: - -================== -VIDEO_CLEAR_BUFFER -================== - -Name ----- - -VIDEO_CLEAR_BUFFER - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_CLEAR_BUFFER - -``int ioctl(fd, VIDEO_CLEAR_BUFFER)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_CLEAR_BUFFER for this command. - -Description ------------ - -This ioctl call clears all video buffers in the driver and in the -decoder hardware. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-command.rst b/drivers/staging/media/av7110/video-command.rst deleted file mode 100644 index cae9445eb3af..000000000000 --- a/drivers/staging/media/av7110/video-command.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_COMMAND: - -============= -VIDEO_COMMAND -============= - -Name ----- - -VIDEO_COMMAND - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_COMMAND - -``int ioctl(int fd, VIDEO_COMMAND, struct video_command *cmd)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_COMMAND for this command. - - - .. row 3 - - - struct video_command \*cmd - - - Commands the decoder. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the -:ref:`VIDIOC_DECODER_CMD` ioctl. - -This ioctl commands the decoder. The ``video_command`` struct is a -subset of the ``v4l2_decoder_cmd`` struct, so refer to the -:ref:`VIDIOC_DECODER_CMD` documentation for -more information. - -.. c:type:: video_command - -.. code-block:: c - - /* The structure must be zeroed before use by the application - This ensures it can be extended safely in the future. */ - struct video_command { - __u32 cmd; - __u32 flags; - union { - struct { - __u64 pts; - } stop; - - struct { - /* 0 or 1000 specifies normal speed, - 1 specifies forward single stepping, - -1 specifies backward single stepping, - >1: playback at speed/1000 of the normal speed, - <-1: reverse playback at (-speed/1000) of the normal speed. */ - __s32 speed; - __u32 format; - } play; - - struct { - __u32 data[16]; - } raw; - }; - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-continue.rst b/drivers/staging/media/av7110/video-continue.rst deleted file mode 100644 index bc34bf3989e4..000000000000 --- a/drivers/staging/media/av7110/video-continue.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_CONTINUE: - -============== -VIDEO_CONTINUE -============== - -Name ----- - -VIDEO_CONTINUE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_CONTINUE - -``int ioctl(fd, VIDEO_CONTINUE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_CONTINUE for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call restarts decoding and playing processes of the video -stream which was played before a call to VIDEO_FREEZE was made. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-fast-forward.rst b/drivers/staging/media/av7110/video-fast-forward.rst deleted file mode 100644 index e71fa8d6965b..000000000000 --- a/drivers/staging/media/av7110/video-fast-forward.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_FAST_FORWARD: - -================== -VIDEO_FAST_FORWARD -================== - -Name ----- - -VIDEO_FAST_FORWARD - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_FAST_FORWARD - -``int ioctl(fd, VIDEO_FAST_FORWARD, int nFrames)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_FAST_FORWARD for this command. - - - .. row 3 - - - int nFrames - - - The number of frames to skip. - -Description ------------ - -This ioctl call asks the Video Device to skip decoding of N number of -I-frames. This call can only be used if VIDEO_SOURCE_MEMORY is -selected. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/av7110/video-fclose.rst b/drivers/staging/media/av7110/video-fclose.rst deleted file mode 100644 index 01d24d548439..000000000000 --- a/drivers/staging/media/av7110/video-fclose.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fclose: - -================= -dvb video close() -================= - -Name ----- - -dvb video close() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int close(int fd) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This system call closes a previously opened video device. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/video-fopen.rst b/drivers/staging/media/av7110/video-fopen.rst deleted file mode 100644 index 1371b083e4e8..000000000000 --- a/drivers/staging/media/av7110/video-fopen.rst +++ /dev/null @@ -1,111 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fopen: - -================ -dvb video open() -================ - -Name ----- - -dvb video open() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int open(const char *deviceName, int flags) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - const char \*deviceName - - - Name of specific video device. - - - .. row 2 - - - int flags - - - A bit-wise OR of the following flags: - - - .. row 3 - - - - - O_RDONLY read-only access - - - .. row 4 - - - - - O_RDWR read/write access - - - .. row 5 - - - - - O_NONBLOCK open in non-blocking mode - - - .. row 6 - - - - - (blocking mode is the default) - -Description ------------ - -This system call opens a named video device (e.g. -/dev/dvb/adapter0/video0) for subsequent use. - -When an open() call has succeeded, the device will be ready for use. The -significance of blocking or non-blocking mode is described in the -documentation for functions where there is a difference. It does not -affect the semantics of the open() call itself. A device opened in -blocking mode can later be put into non-blocking mode (and vice versa) -using the F_SETFL command of the fcntl system call. This is a standard -system call, documented in the Linux manual page for fcntl. Only one -user can open the Video Device in O_RDWR mode. All other attempts to -open the device in this mode will fail, and an error-code will be -returned. If the Video Device is opened in O_RDONLY mode, the only -ioctl call that can be used is VIDEO_GET_STATUS. All other call will -return an error code. - -Return Value ------------- - -.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``ENODEV`` - - - Device driver not loaded/available. - - - .. row 2 - - - ``EINTERNAL`` - - - Internal error. - - - .. row 3 - - - ``EBUSY`` - - - Device or resource busy. - - - .. row 4 - - - ``EINVAL`` - - - Invalid argument. diff --git a/drivers/staging/media/av7110/video-freeze.rst b/drivers/staging/media/av7110/video-freeze.rst deleted file mode 100644 index 4321f257cb70..000000000000 --- a/drivers/staging/media/av7110/video-freeze.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_FREEZE: - -============ -VIDEO_FREEZE -============ - -Name ----- - -VIDEO_FREEZE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_FREEZE - -``int ioctl(fd, VIDEO_FREEZE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_FREEZE for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call suspends the live video stream being played. Decoding -and playing are frozen. It is then possible to restart the decoding and -playing process of the video stream using the VIDEO_CONTINUE command. -If VIDEO_SOURCE_MEMORY is selected in the ioctl call -VIDEO_SELECT_SOURCE, the Digital TV subsystem will not decode any more data -until the ioctl call VIDEO_CONTINUE or VIDEO_PLAY is performed. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-fwrite.rst b/drivers/staging/media/av7110/video-fwrite.rst deleted file mode 100644 index a07fd7d7a40e..000000000000 --- a/drivers/staging/media/av7110/video-fwrite.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fwrite: - -================= -dvb video write() -================= - -Name ----- - -dvb video write() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: size_t write(int fd, const void *buf, size_t count) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - void \*buf - - - Pointer to the buffer containing the PES data. - - - .. row 3 - - - size_t count - - - Size of buf. - -Description ------------ - -This system call can only be used if VIDEO_SOURCE_MEMORY is selected -in the ioctl call VIDEO_SELECT_SOURCE. The data provided shall be in -PES format, unless the capability allows other formats. If O_NONBLOCK -is not specified the function will block until buffer space is -available. The amount of data to be transferred is implied by count. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. - - - .. row 2 - - - ``ENOMEM`` - - - Attempted to write more data than the internal buffer can hold. - - - .. row 3 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/video-get-capabilities.rst b/drivers/staging/media/av7110/video-get-capabilities.rst deleted file mode 100644 index 01e09f56656c..000000000000 --- a/drivers/staging/media/av7110/video-get-capabilities.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_CAPABILITIES: - -====================== -VIDEO_GET_CAPABILITIES -====================== - -Name ----- - -VIDEO_GET_CAPABILITIES - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_CAPABILITIES - -``int ioctl(fd, VIDEO_GET_CAPABILITIES, unsigned int *cap)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_CAPABILITIES for this command. - - - .. row 3 - - - unsigned int \*cap - - - Pointer to a location where to store the capability information. - -Description ------------ - -This ioctl call asks the video device about its decoding capabilities. -On success it returns and integer which has bits set according to the -defines in section ??. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-event.rst b/drivers/staging/media/av7110/video-get-event.rst deleted file mode 100644 index 90382bc36cfe..000000000000 --- a/drivers/staging/media/av7110/video-get-event.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_EVENT: - -=============== -VIDEO_GET_EVENT -=============== - -Name ----- - -VIDEO_GET_EVENT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_EVENT - -``int ioctl(fd, VIDEO_GET_EVENT, struct video_event *ev)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_EVENT for this command. - - - .. row 3 - - - struct video_event \*ev - - - Points to the location where the event, if any, is to be stored. - -Description ------------ - -This ioctl is for Digital TV devices only. To get events from a V4L2 decoder -use the V4L2 :ref:`VIDIOC_DQEVENT` ioctl instead. - -This ioctl call returns an event of type video_event if available. If -an event is not available, the behavior depends on whether the device is -in blocking or non-blocking mode. In the latter case, the call fails -immediately with errno set to ``EWOULDBLOCK``. In the former case, the call -blocks until an event becomes available. The standard Linux poll() -and/or select() system calls can be used with the device file descriptor -to watch for new events. For select(), the file descriptor should be -included in the exceptfds argument, and for poll(), POLLPRI should be -specified as the wake-up condition. Read-only permissions are sufficient -for this ioctl call. - -.. c:type:: video_event - -.. code-block:: c - - struct video_event { - __s32 type; - #define VIDEO_EVENT_SIZE_CHANGED 1 - #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 - #define VIDEO_EVENT_DECODER_STOPPED 3 - #define VIDEO_EVENT_VSYNC 4 - long timestamp; - union { - video_size_t size; - unsigned int frame_rate; /* in frames per 1000sec */ - unsigned char vsync_field; /* unknown/odd/even/progressive */ - } u; - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EWOULDBLOCK`` - - - There is no event pending, and the device is in non-blocking mode. - - - .. row 2 - - - ``EOVERFLOW`` - - - Overflow in event queue - one or more events were lost. diff --git a/drivers/staging/media/av7110/video-get-frame-count.rst b/drivers/staging/media/av7110/video-get-frame-count.rst deleted file mode 100644 index b48ac8c58a41..000000000000 --- a/drivers/staging/media/av7110/video-get-frame-count.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_FRAME_COUNT: - -===================== -VIDEO_GET_FRAME_COUNT -===================== - -Name ----- - -VIDEO_GET_FRAME_COUNT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_FRAME_COUNT - -``int ioctl(int fd, VIDEO_GET_FRAME_COUNT, __u64 *pts)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_FRAME_COUNT for this command. - - - .. row 3 - - - __u64 \*pts - - - Returns the number of frames displayed since the decoder was - started. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_FRAME`` -control. - -This ioctl call asks the Video Device to return the number of displayed -frames since the decoder was started. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-pts.rst b/drivers/staging/media/av7110/video-get-pts.rst deleted file mode 100644 index fedaff41be0b..000000000000 --- a/drivers/staging/media/av7110/video-get-pts.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_PTS: - -============= -VIDEO_GET_PTS -============= - -Name ----- - -VIDEO_GET_PTS - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_PTS - -``int ioctl(int fd, VIDEO_GET_PTS, __u64 *pts)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_PTS for this command. - - - .. row 3 - - - __u64 \*pts - - - Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / - ISO/IEC 13818-1. - - The PTS should belong to the currently played frame if possible, - but may also be a value close to it like the PTS of the last - decoded frame or the last PTS extracted by the PES parser. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_PTS`` -control. - -This ioctl call asks the Video Device to return the current PTS -timestamp. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-size.rst b/drivers/staging/media/av7110/video-get-size.rst deleted file mode 100644 index de34331c5bd1..000000000000 --- a/drivers/staging/media/av7110/video-get-size.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_SIZE: - -============== -VIDEO_GET_SIZE -============== - -Name ----- - -VIDEO_GET_SIZE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_SIZE - -``int ioctl(int fd, VIDEO_GET_SIZE, video_size_t *size)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_SIZE for this command. - - - .. row 3 - - - video_size_t \*size - - - Returns the size and aspect ratio. - -Description ------------ - -This ioctl returns the size and aspect ratio. - -.. c:type:: video_size_t - -.. code-block::c - - typedef struct { - int w; - int h; - video_format_t aspect_ratio; - } video_size_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-status.rst b/drivers/staging/media/av7110/video-get-status.rst deleted file mode 100644 index 9b86fbf411d4..000000000000 --- a/drivers/staging/media/av7110/video-get-status.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_STATUS: - -================ -VIDEO_GET_STATUS -================ - -Name ----- - -VIDEO_GET_STATUS - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_STATUS - -``int ioctl(fd, VIDEO_GET_STATUS, struct video_status *status)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_STATUS for this command. - - - .. row 3 - - - struct video_status \*status - - - Returns the current status of the Video Device. - -Description ------------ - -This ioctl call asks the Video Device to return the current status of -the device. - -.. c:type:: video_status - -.. code-block:: c - - struct video_status { - int video_blank; /* blank video on freeze? */ - video_play_state_t play_state; /* current state of playback */ - video_stream_source_t stream_source; /* current source (demux/memory) */ - video_format_t video_format; /* current aspect ratio of stream*/ - video_displayformat_t display_format;/* selected cropping mode */ - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-play.rst b/drivers/staging/media/av7110/video-play.rst deleted file mode 100644 index 35ac8b98fdbf..000000000000 --- a/drivers/staging/media/av7110/video-play.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_PLAY: - -========== -VIDEO_PLAY -========== - -Name ----- - -VIDEO_PLAY - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_PLAY - -``int ioctl(fd, VIDEO_PLAY)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_PLAY for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call asks the Video Device to start playing a video stream -from the selected source. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-select-source.rst b/drivers/staging/media/av7110/video-select-source.rst deleted file mode 100644 index 929a20985d53..000000000000 --- a/drivers/staging/media/av7110/video-select-source.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SELECT_SOURCE: - -=================== -VIDEO_SELECT_SOURCE -=================== - -Name ----- - -VIDEO_SELECT_SOURCE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SELECT_SOURCE - -``int ioctl(fd, VIDEO_SELECT_SOURCE, video_stream_source_t source)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SELECT_SOURCE for this command. - - - .. row 3 - - - video_stream_source_t source - - - Indicates which source shall be used for the Video stream. - -Description ------------ - -This ioctl is for Digital TV devices only. This ioctl was also supported by the -V4L2 ivtv driver, but that has been replaced by the ivtv-specific -``IVTV_IOC_PASSTHROUGH_MODE`` ioctl. - -This ioctl call informs the video device which source shall be used for -the input data. The possible sources are demux or memory. If memory is -selected, the data is fed to the video device through the write command. - -.. c:type:: video_stream_source_t - -.. code-block:: c - - typedef enum { - VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ - VIDEO_SOURCE_MEMORY /* If this source is selected, the stream - comes from the user through the write - system call */ - } video_stream_source_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-blank.rst b/drivers/staging/media/av7110/video-set-blank.rst deleted file mode 100644 index 70249a6ba125..000000000000 --- a/drivers/staging/media/av7110/video-set-blank.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_BLANK: - -=============== -VIDEO_SET_BLANK -=============== - -Name ----- - -VIDEO_SET_BLANK - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_BLANK - -``int ioctl(fd, VIDEO_SET_BLANK, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_BLANK for this command. - - - .. row 3 - - - boolean mode - - - TRUE: Blank screen when stop. - - - .. row 4 - - - - - FALSE: Show last decoded frame. - -Description ------------ - -This ioctl call asks the Video Device to blank out the picture. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-display-format.rst b/drivers/staging/media/av7110/video-set-display-format.rst deleted file mode 100644 index 1de4f40ae732..000000000000 --- a/drivers/staging/media/av7110/video-set-display-format.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_DISPLAY_FORMAT: - -======================== -VIDEO_SET_DISPLAY_FORMAT -======================== - -Name ----- - -VIDEO_SET_DISPLAY_FORMAT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_DISPLAY_FORMAT - -``int ioctl(fd, VIDEO_SET_DISPLAY_FORMAT)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_DISPLAY_FORMAT for this command. - - - .. row 3 - - - video_display_format_t format - - - Selects the video format to be used. - -Description ------------ - -This ioctl call asks the Video Device to select the video format to be -applied by the MPEG chip on the video. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-format.rst b/drivers/staging/media/av7110/video-set-format.rst deleted file mode 100644 index bb64e37ae081..000000000000 --- a/drivers/staging/media/av7110/video-set-format.rst +++ /dev/null @@ -1,82 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_FORMAT: - -================ -VIDEO_SET_FORMAT -================ - -Name ----- - -VIDEO_SET_FORMAT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_FORMAT - -``int ioctl(fd, VIDEO_SET_FORMAT, video_format_t format)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_FORMAT for this command. - - - .. row 3 - - - video_format_t format - - - video format of TV as defined in section ??. - -Description ------------ - -This ioctl sets the screen format (aspect ratio) of the connected output -device (TV) so that the output of the decoder can be adjusted -accordingly. - -.. c:type:: video_format_t - -.. code-block:: c - - typedef enum { - VIDEO_FORMAT_4_3, /* Select 4:3 format */ - VIDEO_FORMAT_16_9, /* Select 16:9 format. */ - VIDEO_FORMAT_221_1 /* 2.21:1 */ - } video_format_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EINVAL`` - - - format is not a valid video format. diff --git a/drivers/staging/media/av7110/video-set-streamtype.rst b/drivers/staging/media/av7110/video-set-streamtype.rst deleted file mode 100644 index 1f31c048bdbc..000000000000 --- a/drivers/staging/media/av7110/video-set-streamtype.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_STREAMTYPE: - -==================== -VIDEO_SET_STREAMTYPE -==================== - -Name ----- - -VIDEO_SET_STREAMTYPE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_STREAMTYPE - -``int ioctl(fd, VIDEO_SET_STREAMTYPE, int type)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_STREAMTYPE for this command. - - - .. row 3 - - - int type - - - stream type - -Description ------------ - -This ioctl tells the driver which kind of stream to expect being written -to it. If this call is not used the default of video PES is used. Some -drivers might not support this call and always expect PES. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-slowmotion.rst b/drivers/staging/media/av7110/video-slowmotion.rst deleted file mode 100644 index 1478fcc30cb8..000000000000 --- a/drivers/staging/media/av7110/video-slowmotion.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SLOWMOTION: - -================ -VIDEO_SLOWMOTION -================ - -Name ----- - -VIDEO_SLOWMOTION - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SLOWMOTION - -``int ioctl(fd, VIDEO_SLOWMOTION, int nFrames)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SLOWMOTION for this command. - - - .. row 3 - - - int nFrames - - - The number of times to repeat each frame. - -Description ------------ - -This ioctl call asks the video device to repeat decoding frames N number -of times. This call can only be used if VIDEO_SOURCE_MEMORY is -selected. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/av7110/video-stillpicture.rst b/drivers/staging/media/av7110/video-stillpicture.rst deleted file mode 100644 index d25384222a20..000000000000 --- a/drivers/staging/media/av7110/video-stillpicture.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_STILLPICTURE: - -================== -VIDEO_STILLPICTURE -================== - -Name ----- - -VIDEO_STILLPICTURE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_STILLPICTURE - -``int ioctl(fd, VIDEO_STILLPICTURE, struct video_still_picture *sp)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_STILLPICTURE for this command. - - - .. row 3 - - - struct video_still_picture \*sp - - - Pointer to a location where an I-frame and size is stored. - -Description ------------ - -This ioctl call asks the Video Device to display a still picture -(I-frame). The input data shall contain an I-frame. If the pointer is -NULL, then the current displayed still picture is blanked. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-stop.rst b/drivers/staging/media/av7110/video-stop.rst deleted file mode 100644 index 96f61c5b48a2..000000000000 --- a/drivers/staging/media/av7110/video-stop.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_STOP: - -========== -VIDEO_STOP -========== - -Name ----- - -VIDEO_STOP - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_STOP - -``int ioctl(fd, VIDEO_STOP, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_STOP for this command. - - - .. row 3 - - - Boolean mode - - - Indicates how the screen shall be handled. - - - .. row 4 - - - - - TRUE: Blank screen when stop. - - - .. row 5 - - - - - FALSE: Show last decoded frame. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call asks the Video Device to stop playing the current -stream. Depending on the input parameter, the screen can be blanked out -or displaying the last decoded frame. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-try-command.rst b/drivers/staging/media/av7110/video-try-command.rst deleted file mode 100644 index 79bf3dfb8a32..000000000000 --- a/drivers/staging/media/av7110/video-try-command.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_TRY_COMMAND: - -================= -VIDEO_TRY_COMMAND -================= - -Name ----- - -VIDEO_TRY_COMMAND - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_TRY_COMMAND - -``int ioctl(int fd, VIDEO_TRY_COMMAND, struct video_command *cmd)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_TRY_COMMAND for this command. - - - .. row 3 - - - struct video_command \*cmd - - - Try a decoder command. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the -:ref:`VIDIOC_TRY_DECODER_CMD ` ioctl. - -This ioctl tries a decoder command. The ``video_command`` struct is a -subset of the ``v4l2_decoder_cmd`` struct, so refer to the -:ref:`VIDIOC_TRY_DECODER_CMD ` documentation -for more information. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video.rst b/drivers/staging/media/av7110/video.rst deleted file mode 100644 index 808705b769a1..000000000000 --- a/drivers/staging/media/av7110/video.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _dvb_video: - -####################### -Digital TV Video Device -####################### - -The Digital TV video device controls the MPEG2 video decoder of the Digital -TV hardware. It can be accessed through **/dev/dvb/adapter0/video0**. Data -types and ioctl definitions can be accessed by including -**linux/dvb/video.h** in your application. - -Note that the Digital TV video device only controls decoding of the MPEG video -stream, not its presentation on the TV or computer screen. On PCs this -is typically handled by an associated video4linux device, e.g. -**/dev/video**, which allows scaling and defining output windows. - -Some Digital TV cards don't have their own MPEG decoder, which results in the -omission of the audio and video device as well as the video4linux -device. - -The ioctls that deal with SPUs (sub picture units) and navigation -packets are only supported on some MPEG decoders made for DVD playback. - -These ioctls were also used by V4L2 to control MPEG decoders implemented -in V4L2. The use of these ioctls for that purpose has been made obsolete -and proper V4L2 ioctls or controls have been created to replace that -functionality. - - -.. toctree:: - :maxdepth: 1 - - video_types - video_function_calls diff --git a/drivers/staging/media/av7110/video_function_calls.rst b/drivers/staging/media/av7110/video_function_calls.rst deleted file mode 100644 index 20a897be5dca..000000000000 --- a/drivers/staging/media/av7110/video_function_calls.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _video_function_calls: - -******************** -Video Function Calls -******************** - -.. toctree:: - :maxdepth: 1 - - video-fopen - video-fclose - video-fwrite - video-stop - video-play - video-freeze - video-continue - video-select-source - video-set-blank - video-get-status - video-get-frame-count - video-get-pts - video-get-event - video-command - video-try-command - video-get-size - video-set-display-format - video-stillpicture - video-fast-forward - video-slowmotion - video-get-capabilities - video-clear-buffer - video-set-streamtype - video-set-format diff --git a/drivers/staging/media/av7110/video_types.rst b/drivers/staging/media/av7110/video_types.rst deleted file mode 100644 index c4557d328b7a..000000000000 --- a/drivers/staging/media/av7110/video_types.rst +++ /dev/null @@ -1,248 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _video_types: - -**************** -Video Data Types -**************** - - -.. _video-format-t: - -video_format_t -============== - -The ``video_format_t`` data type defined by - - -.. code-block:: c - - typedef enum { - VIDEO_FORMAT_4_3, /* Select 4:3 format */ - VIDEO_FORMAT_16_9, /* Select 16:9 format. */ - VIDEO_FORMAT_221_1 /* 2.21:1 */ - } video_format_t; - -is used in the VIDEO_SET_FORMAT function (??) to tell the driver which -aspect ratio the output hardware (e.g. TV) has. It is also used in the -data structures video_status (??) returned by VIDEO_GET_STATUS (??) -and video_event (??) returned by VIDEO_GET_EVENT (??) which report -about the display format of the current video stream. - - -.. _video-displayformat-t: - -video_displayformat_t -===================== - -In case the display format of the video stream and of the display -hardware differ the application has to specify how to handle the -cropping of the picture. This can be done using the -VIDEO_SET_DISPLAY_FORMAT call (??) which accepts - - -.. code-block:: c - - typedef enum { - VIDEO_PAN_SCAN, /* use pan and scan format */ - VIDEO_LETTER_BOX, /* use letterbox format */ - VIDEO_CENTER_CUT_OUT /* use center cut out format */ - } video_displayformat_t; - -as argument. - - -.. _video-stream-source-t: - -video_stream_source_t -===================== - -The video stream source is set through the VIDEO_SELECT_SOURCE call -and can take the following values, depending on whether we are replaying -from an internal (demuxer) or external (user write) source. - - -.. code-block:: c - - typedef enum { - VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ - VIDEO_SOURCE_MEMORY /* If this source is selected, the stream - comes from the user through the write - system call */ - } video_stream_source_t; - -VIDEO_SOURCE_DEMUX selects the demultiplexer (fed either by the -frontend or the DVR device) as the source of the video stream. If -VIDEO_SOURCE_MEMORY is selected the stream comes from the application -through the **write()** system call. - - -.. _video-play-state-t: - -video_play_state_t -================== - -The following values can be returned by the VIDEO_GET_STATUS call -representing the state of video playback. - - -.. code-block:: c - - typedef enum { - VIDEO_STOPPED, /* Video is stopped */ - VIDEO_PLAYING, /* Video is currently playing */ - VIDEO_FREEZED /* Video is freezed */ - } video_play_state_t; - - -.. c:type:: video_command - -struct video_command -==================== - -The structure must be zeroed before use by the application This ensures -it can be extended safely in the future. - - -.. code-block:: c - - struct video_command { - __u32 cmd; - __u32 flags; - union { - struct { - __u64 pts; - } stop; - - struct { - /* 0 or 1000 specifies normal speed, - 1 specifies forward single stepping, - -1 specifies backward single stepping, - >>1: playback at speed/1000 of the normal speed, - <-1: reverse playback at (-speed/1000) of the normal speed. */ - __s32 speed; - __u32 format; - } play; - - struct { - __u32 data[16]; - } raw; - }; - }; - - -.. _video-size-t: - -video_size_t -============ - - -.. code-block:: c - - typedef struct { - int w; - int h; - video_format_t aspect_ratio; - } video_size_t; - - -.. c:type:: video_event - -struct video_event -================== - -The following is the structure of a video event as it is returned by the -VIDEO_GET_EVENT call. - - -.. code-block:: c - - struct video_event { - __s32 type; - #define VIDEO_EVENT_SIZE_CHANGED 1 - #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 - #define VIDEO_EVENT_DECODER_STOPPED 3 - #define VIDEO_EVENT_VSYNC 4 - long timestamp; - union { - video_size_t size; - unsigned int frame_rate; /* in frames per 1000sec */ - unsigned char vsync_field; /* unknown/odd/even/progressive */ - } u; - }; - - -.. c:type:: video_status - -struct video_status -=================== - -The VIDEO_GET_STATUS call returns the following structure informing -about various states of the playback operation. - - -.. code-block:: c - - struct video_status { - int video_blank; /* blank video on freeze? */ - video_play_state_t play_state; /* current state of playback */ - video_stream_source_t stream_source; /* current source (demux/memory) */ - video_format_t video_format; /* current aspect ratio of stream */ - video_displayformat_t display_format;/* selected cropping mode */ - }; - -If video_blank is set video will be blanked out if the channel is -changed or if playback is stopped. Otherwise, the last picture will be -displayed. play_state indicates if the video is currently frozen, -stopped, or being played back. The stream_source corresponds to the -selected source for the video stream. It can come either from the -demultiplexer or from memory. The video_format indicates the aspect -ratio (one of 4:3 or 16:9) of the currently played video stream. -Finally, display_format corresponds to the selected cropping mode in -case the source video format is not the same as the format of the output -device. - - -.. c:type:: video_still_picture - -struct video_still_picture -========================== - -An I-frame displayed via the VIDEO_STILLPICTURE call is passed on -within the following structure. - - -.. code-block:: c - - /* pointer to and size of a single iframe in memory */ - struct video_still_picture { - char *iFrame; /* pointer to a single iframe in memory */ - int32_t size; - }; - - -.. _video_caps: - -video capabilities -================== - -A call to VIDEO_GET_CAPABILITIES returns an unsigned integer with the -following bits set according to the hardwares capabilities. - - -.. code-block:: c - - /* bit definitions for capabilities: */ - /* can the hardware decode MPEG1 and/or MPEG2? */ - #define VIDEO_CAP_MPEG1 1 - #define VIDEO_CAP_MPEG2 2 - /* can you send a system and/or program stream to video device? - (you still have to open the video and the audio device but only - send the stream to the video device) */ - #define VIDEO_CAP_SYS 4 - #define VIDEO_CAP_PROG 8 - /* can the driver also handle SPU, NAVI and CSS encoded data? - (CSS API is not present yet) */ - #define VIDEO_CAP_SPU 16 - #define VIDEO_CAP_NAVI 32 - #define VIDEO_CAP_CSS 64 diff --git a/drivers/staging/media/deprecated/saa7146/Kconfig b/drivers/staging/media/deprecated/saa7146/Kconfig index d0cb52164ff8..54154da79f59 100644 --- a/drivers/staging/media/deprecated/saa7146/Kconfig +++ b/drivers/staging/media/deprecated/saa7146/Kconfig @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 source "drivers/staging/media/deprecated/saa7146/common/Kconfig" +source "drivers/staging/media/deprecated/saa7146/av7110/Kconfig" source "drivers/staging/media/deprecated/saa7146/saa7146/Kconfig" source "drivers/staging/media/deprecated/saa7146/ttpci/Kconfig" diff --git a/drivers/staging/media/deprecated/saa7146/Makefile b/drivers/staging/media/deprecated/saa7146/Makefile index 9d99fdedf813..68e7aa10c639 100644 --- a/drivers/staging/media/deprecated/saa7146/Makefile +++ b/drivers/staging/media/deprecated/saa7146/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += common/ saa7146/ ttpci/ +obj-y += common/ av7110/ saa7146/ ttpci/ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/Kconfig b/drivers/staging/media/deprecated/saa7146/av7110/Kconfig new file mode 100644 index 000000000000..1571eab31926 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/Kconfig @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_AV7110_IR + bool + depends on RC_CORE=y || RC_CORE = DVB_AV7110 + default DVB_AV7110 + +config DVB_AV7110 + tristate "AV7110 cards (DEPRECATED)" + depends on DVB_CORE && PCI && I2C + select TTPCI_EEPROM + select VIDEO_SAA7146_VV + depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + help + Support for SAA7146 and AV7110 based DVB cards as produced + by Fujitsu-Siemens, Technotrend, Hauppauge and others. + + This driver only supports the fullfeatured cards with + onboard MPEG2 decoder. + + This driver needs an external firmware. Please use the script + "/scripts/get_dvb_firmware av7110" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + + Alternatively, you can download the file and use the kernel's + EXTRA_FIRMWARE configuration option to build it into your + kernel image by adding the filename to the EXTRA_FIRMWARE + configuration option string. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + +config DVB_AV7110_OSD + bool "AV7110 OSD support (DEPRECATED)" + depends on DVB_AV7110 + default y if DVB_AV7110=y || DVB_AV7110=m + help + The AV7110 firmware provides some code to generate an OnScreenDisplay + on the video output. This is kind of nonstandard and not guaranteed to + be maintained. + + Anyway, some popular DVB software like VDR uses this OSD to render + its menus, so say Y if you want to use this software. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + All other people say N. + +config DVB_BUDGET_PATCH + tristate "AV7110 cards with Budget Patch (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + depends on DVB_AV7110 + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + help + Support for Budget Patch (full TS) modification on + SAA7146+AV7110 based cards (DVB-S cards). This + driver doesn't use onboard MPEG2 decoder. The + card is driven in Budget-only mode. Card is + required to have loaded firmware to tune properly. + Firmware can be loaded by insertion and removal of + standard AV7110 driver prior to loading this + driver. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-patch. + +if DVB_AV7110 + +# Frontend driver that it is used only by AV7110 driver +# While technically independent, it doesn't make sense to keep +# it if we drop support for AV7110, as no other driver will use it. + +config DVB_SP8870 + tristate "Spase sp8870 based (DEPRECATED)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + This driver needs external firmware. Please use the command + "/scripts/get_dvb_firmware sp8870" to + download/extract it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + +endif diff --git a/drivers/staging/media/deprecated/saa7146/av7110/Makefile b/drivers/staging/media/deprecated/saa7146/av7110/Makefile new file mode 100644 index 000000000000..c04cd0a59109 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the AV7110 DVB device driver +# + +dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o \ + av7110_ipack.o dvb_filter.o + +ifdef CONFIG_DVB_AV7110_IR +dvb-ttpci-objs += av7110_ir.o +endif + +obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o + +obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o + +obj-$(CONFIG_DVB_SP8870) += sp8870.o + +ccflags-y += -I $(srctree)/drivers/media/dvb-frontends +ccflags-y += -I $(srctree)/drivers/media/tuners +ccflags-y += -I $(srctree)/drivers/media/common +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/ttpci +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/common diff --git a/drivers/staging/media/deprecated/saa7146/av7110/TODO b/drivers/staging/media/deprecated/saa7146/av7110/TODO new file mode 100644 index 000000000000..38817e04bb67 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/TODO @@ -0,0 +1,9 @@ +- This driver is too old and relies on a different API. + Drop it from Kernel on a couple of versions. +- Cleanup patches for the drivers here won't be accepted. + +These drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst new file mode 100644 index 000000000000..33b5363317f1 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst @@ -0,0 +1,58 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_BILINGUAL_CHANNEL_SELECT: + +============================== +AUDIO_BILINGUAL_CHANNEL_SELECT +============================== + +Name +---- + +AUDIO_BILINGUAL_CHANNEL_SELECT + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_BILINGUAL_CHANNEL_SELECT + +``int ioctl(int fd, AUDIO_BILINGUAL_CHANNEL_SELECT, struct audio_channel_select *select)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - audio_channel_select_t ch + + - Select the output format of the audio (mono left/right, stereo). + +Description +----------- + +This ioctl is obsolete. Do not use in new drivers. It has been replaced +by the V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` control +for MPEG decoders controlled through V4L2. + +This ioctl call asks the Audio Device to select the requested channel +for bilingual streams if possible. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst new file mode 100644 index 000000000000..74093df92a68 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_CHANNEL_SELECT: + +==================== +AUDIO_CHANNEL_SELECT +==================== + +Name +---- + +AUDIO_CHANNEL_SELECT + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_CHANNEL_SELECT + +``int ioctl(int fd, AUDIO_CHANNEL_SELECT, struct audio_channel_select *select)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - audio_channel_select_t ch + + - Select the output format of the audio (mono left/right, stereo). + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` control instead. + +This ioctl call asks the Audio Device to select the requested channel if +possible. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst new file mode 100644 index 000000000000..a0ebb0278260 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_CLEAR_BUFFER: + +================== +AUDIO_CLEAR_BUFFER +================== + +Name +---- + +AUDIO_CLEAR_BUFFER + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_CLEAR_BUFFER + +``int ioctl(int fd, AUDIO_CLEAR_BUFFER)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This ioctl call asks the Audio Device to clear all software and hardware +buffers of the audio decoder device. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst new file mode 100644 index 000000000000..a2e9850f37f2 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_CONTINUE: + +============== +AUDIO_CONTINUE +============== + +Name +---- + +AUDIO_CONTINUE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_CONTINUE + +``int ioctl(int fd, AUDIO_CONTINUE)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This ioctl restarts the decoding and playing process previously paused +with AUDIO_PAUSE command. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst new file mode 100644 index 000000000000..77857d578e83 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _audio_fclose: + +======================== +Digital TV audio close() +======================== + +Name +---- + +Digital TV audio close() + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:function:: int close(int fd) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This system call closes a previously opened audio device. + +Return Value +------------ + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EBADF`` + + - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst new file mode 100644 index 000000000000..774daaab3bad --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst @@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _audio_fopen: + +======================= +Digital TV audio open() +======================= + +Name +---- + +Digital TV audio open() + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:function:: int open(const char *deviceName, int flags) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - const char \*deviceName + + - Name of specific audio device. + + - .. row 2 + + - int flags + + - A bit-wise OR of the following flags: + + - .. row 3 + + - + - O_RDONLY read-only access + + - .. row 4 + + - + - O_RDWR read/write access + + - .. row 5 + + - + - O_NONBLOCK open in non-blocking mode + + - .. row 6 + + - + - (blocking mode is the default) + +Description +----------- + +This system call opens a named audio device (e.g. +/dev/dvb/adapter0/audio0) for subsequent use. When an open() call has +succeeded, the device will be ready for use. The significance of +blocking or non-blocking mode is described in the documentation for +functions where there is a difference. It does not affect the semantics +of the open() call itself. A device opened in blocking mode can later be +put into non-blocking mode (and vice versa) using the F_SETFL command +of the fcntl system call. This is a standard system call, documented in +the Linux manual page for fcntl. Only one user can open the Audio Device +in O_RDWR mode. All other attempts to open the device in this mode will +fail, and an error code will be returned. If the Audio Device is opened +in O_RDONLY mode, the only ioctl call that can be used is +AUDIO_GET_STATUS. All other call will return with an error code. + +Return Value +------------ + +.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``ENODEV`` + + - Device driver not loaded/available. + + - .. row 2 + + - ``EBUSY`` + + - Device or resource busy. + + - .. row 3 + + - ``EINVAL`` + + - Invalid argument. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst new file mode 100644 index 000000000000..7b096ac2b6c4 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst @@ -0,0 +1,79 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _audio_fwrite: + +========================= +Digital TV audio write() +========================= + +Name +---- + +Digital TV audio write() + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:function:: size_t write(int fd, const void *buf, size_t count) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - void \*buf + + - Pointer to the buffer containing the PES data. + + - .. row 3 + + - size_t count + + - Size of buf. + +Description +----------- + +This system call can only be used if AUDIO_SOURCE_MEMORY is selected +in the ioctl call AUDIO_SELECT_SOURCE. The data provided shall be in +PES format. If O_NONBLOCK is not specified the function will block +until buffer space is available. The amount of data to be transferred is +implied by count. + +Return Value +------------ + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EPERM`` + + - Mode AUDIO_SOURCE_MEMORY not selected. + + - .. row 2 + + - ``ENOMEM`` + + - Attempted to write more data than the internal buffer can hold. + + - .. row 3 + + - ``EBADF`` + + - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst new file mode 100644 index 000000000000..6d9eb71dad17 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_GET_CAPABILITIES: + +====================== +AUDIO_GET_CAPABILITIES +====================== + +Name +---- + +AUDIO_GET_CAPABILITIES + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_GET_CAPABILITIES + +``int ioctl(int fd, AUDIO_GET_CAPABILITIES, unsigned int *cap)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - unsigned int \*cap + + - Returns a bit array of supported sound formats. + +Description +----------- + +This ioctl call asks the Audio Device to tell us about the decoding +capabilities of the audio hardware. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst new file mode 100644 index 000000000000..7ae8db2e65e9 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_GET_STATUS: + +================ +AUDIO_GET_STATUS +================ + +Name +---- + +AUDIO_GET_STATUS + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_GET_STATUS + +``int ioctl(int fd, AUDIO_GET_STATUS, struct audio_status *status)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - struct audio_status \*status + + - Returns the current state of Audio Device. + +Description +----------- + +This ioctl call asks the Audio Device to return the current state of the +Audio Device. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst new file mode 100644 index 000000000000..d37d1ddce4df --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_PAUSE: + +=========== +AUDIO_PAUSE +=========== + +Name +---- + +AUDIO_PAUSE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_PAUSE + +``int ioctl(int fd, AUDIO_PAUSE)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This ioctl call suspends the audio stream being played. Decoding and +playing are paused. It is then possible to restart again decoding and +playing process of the audio stream using AUDIO_CONTINUE command. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst new file mode 100644 index 000000000000..e591930b6ca7 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_PLAY: + +========== +AUDIO_PLAY +========== + +Name +---- + +AUDIO_PLAY + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_PLAY + +``int ioctl(int fd, AUDIO_PLAY)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This ioctl call asks the Audio Device to start playing an audio stream +from the selected source. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst new file mode 100644 index 000000000000..6a0c0f365eb1 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SELECT_SOURCE: + +=================== +AUDIO_SELECT_SOURCE +=================== + +Name +---- + +AUDIO_SELECT_SOURCE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SELECT_SOURCE + +``int ioctl(int fd, AUDIO_SELECT_SOURCE, struct audio_stream_source *source)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - audio_stream_source_t source + + - Indicates the source that shall be used for the Audio stream. + +Description +----------- + +This ioctl call informs the audio device which source shall be used for +the input data. The possible sources are demux or memory. If +AUDIO_SOURCE_MEMORY is selected, the data is fed to the Audio Device +through the write command. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst new file mode 100644 index 000000000000..85a8016bf025 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst @@ -0,0 +1,58 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_AV_SYNC: + +================= +AUDIO_SET_AV_SYNC +================= + +Name +---- + +AUDIO_SET_AV_SYNC + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_AV_SYNC + +``int ioctl(int fd, AUDIO_SET_AV_SYNC, boolean state)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - boolean state + + - Tells the Digital TV subsystem if A/V synchronization shall be ON or OFF. + + TRUE: AV-sync ON + + FALSE: AV-sync OFF + +Description +----------- + +This ioctl call asks the Audio Device to turn ON or OFF A/V +synchronization. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst new file mode 100644 index 000000000000..80d551a2053a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_BYPASS_MODE: + +===================== +AUDIO_SET_BYPASS_MODE +===================== + +Name +---- + +AUDIO_SET_BYPASS_MODE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_BYPASS_MODE + +``int ioctl(int fd, AUDIO_SET_BYPASS_MODE, boolean mode)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - boolean mode + + - Enables or disables the decoding of the current Audio stream in + the Digital TV subsystem. + + TRUE: Bypass is disabled + + FALSE: Bypass is enabled + +Description +----------- + +This ioctl call asks the Audio Device to bypass the Audio decoder and +forward the stream without decoding. This mode shall be used if streams +that can't be handled by the Digital TV system shall be decoded. Dolby +DigitalTM streams are automatically forwarded by the Digital TV subsystem if +the hardware can handle it. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst new file mode 100644 index 000000000000..39ad846d412d --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst @@ -0,0 +1,59 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_ID: + +============ +AUDIO_SET_ID +============ + +Name +---- + +AUDIO_SET_ID + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_ID + +``int ioctl(int fd, AUDIO_SET_ID, int id)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - int id + + - audio sub-stream id + +Description +----------- + +This ioctl selects which sub-stream is to be decoded if a program or +system stream is sent to the video device. If no audio stream type is +set the id has to be in [0xC0,0xDF] for MPEG sound, in [0x80,0x87] for +AC3 and in [0xA0,0xA7] for LPCM. More specifications may follow for +other stream types. If the stream type is set the id just specifies the +substream id of the audio stream and only the first 5 bits are +recognized. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst new file mode 100644 index 000000000000..45dbdf4801e0 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst @@ -0,0 +1,53 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_MIXER: + +=============== +AUDIO_SET_MIXER +=============== + +Name +---- + +AUDIO_SET_MIXER + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_MIXER + +``int ioctl(int fd, AUDIO_SET_MIXER, struct audio_mixer *mix)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - audio_mixer_t \*mix + + - mixer settings. + +Description +----------- + +This ioctl lets you adjust the mixer settings of the audio decoder. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst new file mode 100644 index 000000000000..987751f92967 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_MUTE: + +============== +AUDIO_SET_MUTE +============== + +Name +---- + +AUDIO_SET_MUTE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_MUTE + +``int ioctl(int fd, AUDIO_SET_MUTE, boolean state)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - boolean state + + - Indicates if audio device shall mute or not. + + TRUE: Audio Mute + + FALSE: Audio Un-mute + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 :ref:`VIDIOC_DECODER_CMD` with the +``V4L2_DEC_CMD_START_MUTE_AUDIO`` flag instead. + +This ioctl call asks the audio device to mute the stream that is +currently being played. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst new file mode 100644 index 000000000000..77d73c74882f --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst @@ -0,0 +1,66 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_SET_STREAMTYPE: + +==================== +AUDIO_SET_STREAMTYPE +==================== + +Name +---- + +AUDIO_SET_STREAMTYPE + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_SET_STREAMTYPE + +``int ioctl(fd, AUDIO_SET_STREAMTYPE, int type)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - + + - int fd + + - File descriptor returned by a previous call to open(). + + - + + - int type + + - stream type + +Description +----------- + +This ioctl tells the driver which kind of audio stream to expect. This +is useful if the stream offers several audio sub-streams like LPCM and +AC3. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EINVAL`` + + - type is not a valid or supported stream type. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst new file mode 100644 index 000000000000..d77f786fd797 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.audio + +.. _AUDIO_STOP: + +========== +AUDIO_STOP +========== + +Name +---- + +AUDIO_STOP + +.. attention:: This ioctl is deprecated + +Synopsis +-------- + +.. c:macro:: AUDIO_STOP + +``int ioctl(int fd, AUDIO_STOP)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This ioctl call asks the Audio Device to stop playing the current +stream. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio.rst new file mode 100644 index 000000000000..aa753336b31f --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio.rst @@ -0,0 +1,27 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _dvb_audio: + +####################### +Digital TV Audio Device +####################### + +The Digital TV audio device controls the MPEG2 audio decoder of the Digital +TV hardware. It can be accessed through ``/dev/dvb/adapter?/audio?``. Data +types and ioctl definitions can be accessed by including +``linux/dvb/audio.h`` in your application. + +Please note that some Digital TV cards don't have their own MPEG decoder, which +results in the omission of the audio and video device. + +These ioctls were also used by V4L2 to control MPEG decoders implemented +in V4L2. The use of these ioctls for that purpose has been made obsolete +and proper V4L2 ioctls or controls have been created to replace that +functionality. + + +.. toctree:: + :maxdepth: 1 + + audio_data_types + audio_function_calls diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst new file mode 100644 index 000000000000..4744529136a8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst @@ -0,0 +1,116 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _audio_data_types: + +**************** +Audio Data Types +**************** + +This section describes the structures, data types and defines used when +talking to the audio device. + +.. c:type:: audio_stream_source + +The audio stream source is set through the AUDIO_SELECT_SOURCE call +and can take the following values, depending on whether we are replaying +from an internal (demux) or external (user write) source. + + +.. code-block:: c + + typedef enum { + AUDIO_SOURCE_DEMUX, + AUDIO_SOURCE_MEMORY + } audio_stream_source_t; + +AUDIO_SOURCE_DEMUX selects the demultiplexer (fed either by the +frontend or the DVR device) as the source of the video stream. If +AUDIO_SOURCE_MEMORY is selected the stream comes from the application +through the ``write()`` system call. + + +.. c:type:: audio_play_state + +The following values can be returned by the AUDIO_GET_STATUS call +representing the state of audio playback. + + +.. code-block:: c + + typedef enum { + AUDIO_STOPPED, + AUDIO_PLAYING, + AUDIO_PAUSED + } audio_play_state_t; + + +.. c:type:: audio_channel_select + +The audio channel selected via AUDIO_CHANNEL_SELECT is determined by +the following values. + + +.. code-block:: c + + typedef enum { + AUDIO_STEREO, + AUDIO_MONO_LEFT, + AUDIO_MONO_RIGHT, + AUDIO_MONO, + AUDIO_STEREO_SWAPPED + } audio_channel_select_t; + + +.. c:type:: audio_status + +The AUDIO_GET_STATUS call returns the following structure informing +about various states of the playback operation. + + +.. code-block:: c + + typedef struct audio_status { + boolean AV_sync_state; + boolean mute_state; + audio_play_state_t play_state; + audio_stream_source_t stream_source; + audio_channel_select_t channel_select; + boolean bypass_mode; + audio_mixer_t mixer_state; + } audio_status_t; + + +.. c:type:: audio_mixer + +The following structure is used by the AUDIO_SET_MIXER call to set the +audio volume. + + +.. code-block:: c + + typedef struct audio_mixer { + unsigned int volume_left; + unsigned int volume_right; + } audio_mixer_t; + + +.. _audio_encodings: + +audio encodings +=============== + +A call to AUDIO_GET_CAPABILITIES returns an unsigned integer with the +following bits set according to the hardwares capabilities. + + +.. code-block:: c + + #define AUDIO_CAP_DTS 1 + #define AUDIO_CAP_LPCM 2 + #define AUDIO_CAP_MP1 4 + #define AUDIO_CAP_MP2 8 + #define AUDIO_CAP_MP3 16 + #define AUDIO_CAP_AAC 32 + #define AUDIO_CAP_OGG 64 + #define AUDIO_CAP_SDDS 128 + #define AUDIO_CAP_AC3 256 diff --git a/drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst new file mode 100644 index 000000000000..fa5ba9539caf --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst @@ -0,0 +1,30 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _audio_function_calls: + +******************** +Audio Function Calls +******************** + +.. toctree:: + :maxdepth: 1 + + audio-fopen + audio-fclose + audio-fwrite + audio-stop + audio-play + audio-pause + audio-continue + audio-select-source + audio-set-mute + audio-set-av-sync + audio-set-bypass-mode + audio-channel-select + audio-bilingual-channel-select + audio-get-status + audio-get-capabilities + audio-clear-buffer + audio-set-id + audio-set-mixer + audio-set-streamtype diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110.c new file mode 100644 index 000000000000..df81a9b744c2 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110.c @@ -0,0 +1,2919 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) + * av7110.c: initialization and demux stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * the project's page is at https://linuxtv.org + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include + +#include "ttpci-eeprom.h" +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" +#include "av7110_ca.h" +#include "av7110_ipack.h" + +#include "bsbe1.h" +#include "lnbp21.h" +#include "bsru6.h" + +#define TS_WIDTH 376 +#define TS_HEIGHT 512 +#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) +#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) + + +int av7110_debug; + +static int vidmode = CVBS_RGB_OUT; +static int pids_off; +static int adac = DVB_ADAC_TI; +static int hw_sections; +static int rgb_on; +static int volume = 255; +static int budgetpatch; +static int wss_cfg_4_3 = 0x4008; +static int wss_cfg_16_9 = 0x0007; +static int tv_standard; +static int full_ts; + +module_param_named(debug, av7110_debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); +module_param(vidmode, int, 0444); +MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); +module_param(pids_off, int, 0444); +MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); +module_param(adac, int, 0444); +MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); +module_param(hw_sections, int, 0444); +MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); +module_param(rgb_on, int, 0444); +MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); +module_param(volume, int, 0444); +MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); +module_param(budgetpatch, int, 0444); +MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); +module_param(full_ts, int, 0444); +MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); +module_param(wss_cfg_4_3, int, 0444); +MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(wss_cfg_16_9, int, 0444); +MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(tv_standard, int, 0444); +MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void restart_feeds(struct av7110 *av7110); +static int budget_start_feed(struct dvb_demux_feed *feed); +static int budget_stop_feed(struct dvb_demux_feed *feed); + +static int av7110_num; + +#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ +{\ + if (fe_func != NULL) { \ + av7110_copy = fe_func; \ + fe_func = av7110_func; \ + } \ +} + + +static void init_av7110_av(struct av7110 *av7110) +{ + int ret; + struct saa7146_dev *dev = av7110->dev; + + /* set internal volume control to maximum */ + av7110->adac_type = DVB_ADAC_TI; + ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); + if (ret < 0) + printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) av7110->display_ar); + if (ret < 0) + printk("dvb-ttpci: unable to set aspect ratio\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, av7110->display_panscan); + if (ret < 0) + printk("dvb-ttpci: unable to set pan scan\n"); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); + if (ret < 0) + printk("dvb-ttpci: unable to configure 4:3 wss\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); + if (ret < 0) + printk("dvb-ttpci: unable to configure 16:9 wss\n"); + + ret = av7710_set_video_mode(av7110, vidmode); + if (ret < 0) + printk("dvb-ttpci:cannot set video mode:%d\n",ret); + + /* handle different card types */ + /* remaining inits according to card and frontend type */ + av7110->analog_tuner_flags = 0; + av7110->current_input = 0; + if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) + av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on + if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { + printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_CRYSTAL; + i2c_writereg(av7110, 0x20, 0x01, 0xd2); + i2c_writereg(av7110, 0x20, 0x02, 0x49); + i2c_writereg(av7110, 0x20, 0x03, 0x00); + i2c_writereg(av7110, 0x20, 0x04, 0x00); + + /** + * some special handling for the Siemens DVB-C cards... + */ + } else if (0 == av7110_init_analog_module(av7110)) { + /* done. */ + } + else if (dev->pci->subsystem_vendor == 0x110a) { + printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_NONE; + } + else { + av7110->adac_type = adac; + printk("dvb-ttpci: adac type set to %d @ card %d\n", + av7110->adac_type, av7110->dvb_adapter.num); + } + + if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { + // switch DVB SCART on + ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); + if (ret < 0) + printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); + ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); + if (ret < 0) + printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); + if (rgb_on && + ((av7110->dev->pci->subsystem_vendor == 0x110a) || + (av7110->dev->pci->subsystem_vendor == 0x13c2)) && + (av7110->dev->pci->subsystem_device == 0x0000)) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 + //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 + } + } + + if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) + av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on + + ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); + if (ret < 0) + printk("dvb-ttpci:cannot set volume :%d\n",ret); +} + +static void recover_arm(struct av7110 *av7110) +{ + dprintk(4, "%p\n",av7110); + + av7110_bootarm(av7110); + msleep(100); + + init_av7110_av(av7110); + + /* card-specific recovery */ + if (av7110->recover) + av7110->recover(av7110); + + restart_feeds(av7110); + +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) + av7110_set_ir_config(av7110); +#endif +} + +static void av7110_arm_sync(struct av7110 *av7110) +{ + if (av7110->arm_thread) + kthread_stop(av7110->arm_thread); + + av7110->arm_thread = NULL; +} + +static int arm_thread(void *data) +{ + struct av7110 *av7110 = data; + u16 newloops = 0; + int timeout; + + dprintk(4, "%p\n",av7110); + + for (;;) { + timeout = wait_event_interruptible_timeout(av7110->arm_wait, + kthread_should_stop(), 5 * HZ); + + if (-ERESTARTSYS == timeout || kthread_should_stop()) { + /* got signal or told to quit*/ + break; + } + + if (!av7110->arm_ready) + continue; + + if (mutex_lock_interruptible(&av7110->dcomlock)) + break; + newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); + mutex_unlock(&av7110->dcomlock); + + if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { + printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", + av7110->dvb_adapter.num); + + recover_arm(av7110); + + if (mutex_lock_interruptible(&av7110->dcomlock)) + break; + newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; + mutex_unlock(&av7110->dcomlock); + } + av7110->arm_loops = newloops; + av7110->arm_errors = 0; + } + + return 0; +} + + +/**************************************************************************** + * IRQ handling + ****************************************************************************/ + +static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + struct dvb_demux_filter *dvbdmxfilter, + struct av7110 *av7110) +{ + if (!dvbdmxfilter->feed->demux->dmx.frontend) + return 0; + if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) + return 0; + + switch (dvbdmxfilter->type) { + case DMX_TYPE_SEC: + if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) + return 0; + if (dvbdmxfilter->doneq) { + struct dmx_section_filter *filter = &dvbdmxfilter->filter; + int i; + u8 xor, neq = 0; + + for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { + xor = filter->filter_value[i] ^ buffer1[i]; + neq |= dvbdmxfilter->maskandnotmode[i] & xor; + } + if (!neq) + return 0; + } + return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, + buffer2, buffer2_len, + &dvbdmxfilter->filter, NULL); + case DMX_TYPE_TS: + if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) + return 0; + if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) + return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, + buffer2, buffer2_len, + &dvbdmxfilter->feed->feed.ts, + NULL); + else + av7110_p2t_write(buffer1, buffer1_len, + dvbdmxfilter->feed->pid, + &av7110->p2t_filter[dvbdmxfilter->index]); + return 0; + default: + return 0; + } +} + + +//#define DEBUG_TIMING +static inline void print_time(char *s) +{ +#ifdef DEBUG_TIMING + struct timespec64 ts; + ktime_get_real_ts64(&ts); + printk("%s: %lld.%09ld\n", s, (s64)ts.tv_sec, ts.tv_nsec); +#endif +} + +#define DEBI_READ 0 +#define DEBI_WRITE 1 +static inline void start_debi_dma(struct av7110 *av7110, int dir, + unsigned long addr, unsigned int len) +{ + dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); + if (saa7146_wait_for_debi_done(av7110->dev, 0)) { + printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + return; + } + + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ + SAA7146_IER_ENABLE(av7110->dev, MASK_19); + if (len < 5) + len = 5; /* we want a real DEBI DMA */ + if (dir == DEBI_WRITE) + iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); + else + irdebi(av7110, DEBISWAB, addr, 0, len); +} + +static void debiirq(struct tasklet_struct *t) +{ + struct av7110 *av7110 = from_tasklet(av7110, t, debi_tasklet); + int type = av7110->debitype; + int handle = (type >> 8) & 0x1f; + unsigned int xfer = 0; + + print_time("debi"); + dprintk(4, "type 0x%04x\n", type); + + if (type == -1) { + printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", + jiffies, saa7146_read(av7110->dev, PSR), + saa7146_read(av7110->dev, SSR)); + goto debi_done; + } + av7110->debitype = -1; + + switch (type & 0xff) { + + case DATA_TS_RECORD: + dvb_dmx_swfilter_packets(&av7110->demux, + (const u8 *) av7110->debi_virt, + av7110->debilen / 188); + xfer = RX_BUFF; + break; + + case DATA_PES_RECORD: + if (av7110->demux.recording) + av7110_record_cb(&av7110->p2t[handle], + (u8 *) av7110->debi_virt, + av7110->debilen); + xfer = RX_BUFF; + break; + + case DATA_IPMPE: + case DATA_FSECTION: + case DATA_PIPING: + if (av7110->handle2filter[handle]) + DvbDmxFilterCallback((u8 *)av7110->debi_virt, + av7110->debilen, NULL, 0, + av7110->handle2filter[handle], + av7110); + xfer = RX_BUFF; + break; + + case DATA_CI_GET: + { + u8 *data = av7110->debi_virt; + u8 data_0 = data[0]; + + if (data_0 < 2 && data[2] == 0xff) { + int flags = 0; + if (data[5] > 0) + flags |= CA_CI_MODULE_PRESENT; + if (data[5] > 5) + flags |= CA_CI_MODULE_READY; + av7110->ci_slot[data_0].flags = flags; + } else + ci_get_data(&av7110->ci_rbuffer, + av7110->debi_virt, + av7110->debilen); + xfer = RX_BUFF; + break; + } + + case DATA_COMMON_INTERFACE: + CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); + xfer = RX_BUFF; + break; + + case DATA_DEBUG_MESSAGE: + ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; + printk("%s\n", (s8 *) av7110->debi_virt); + xfer = RX_BUFF; + break; + + case DATA_CI_PUT: + dprintk(4, "debi DATA_CI_PUT\n"); + xfer = TX_BUFF; + break; + case DATA_MPEG_PLAY: + dprintk(4, "debi DATA_MPEG_PLAY\n"); + xfer = TX_BUFF; + break; + case DATA_BMP_LOAD: + dprintk(4, "debi DATA_BMP_LOAD\n"); + xfer = TX_BUFF; + break; + default: + break; + } +debi_done: + spin_lock(&av7110->debilock); + if (xfer) + iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); + ARM_ClearMailBox(av7110); + spin_unlock(&av7110->debilock); +} + +/* irq from av7110 firmware writing the mailbox register in the DPRAM */ +static void gpioirq(struct tasklet_struct *t) +{ + struct av7110 *av7110 = from_tasklet(av7110, t, gpio_tasklet); + u32 rxbuf, txbuf; + int len; + + if (av7110->debitype != -1) + /* we shouldn't get any irq while a debi xfer is running */ + printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", + jiffies, saa7146_read(av7110->dev, PSR), + saa7146_read(av7110->dev, SSR)); + + if (saa7146_wait_for_debi_done(av7110->dev, 0)) { + printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + BUG(); /* maybe we should try resetting the debi? */ + } + + spin_lock(&av7110->debilock); + ARM_ClearIrq(av7110); + + /* see what the av7110 wants */ + av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); + av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + len = (av7110->debilen + 3) & ~3; + + print_time("gpio"); + dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); + + switch (av7110->debitype & 0xff) { + + case DATA_TS_PLAY: + case DATA_PES_PLAY: + break; + + case DATA_MPEG_VIDEO_EVENT: + { + u32 h_ar; + struct video_event event; + + av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); + h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); + + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + + av7110->video_size.h = h_ar & 0xfff; + + event.type = VIDEO_EVENT_SIZE_CHANGED; + event.u.size.w = av7110->video_size.w; + event.u.size.h = av7110->video_size.h; + switch ((h_ar >> 12) & 0xf) + { + case 3: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; + event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; + av7110->videostate.video_format = VIDEO_FORMAT_16_9; + break; + case 4: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; + event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; + av7110->videostate.video_format = VIDEO_FORMAT_221_1; + break; + default: + av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; + event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; + av7110->videostate.video_format = VIDEO_FORMAT_4_3; + } + + dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", + av7110->video_size.w, av7110->video_size.h, + av7110->video_size.aspect_ratio); + + dvb_video_add_event(av7110, &event); + break; + } + + case DATA_CI_PUT: + { + int avail; + struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; + + avail = dvb_ringbuffer_avail(cibuf); + if (avail <= 2) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; + len |= DVB_RINGBUFFER_PEEK(cibuf, 1); + if (avail < len + 2) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + DVB_RINGBUFFER_SKIP(cibuf, 2); + + dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); + + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + dprintk(8, "DMA: CI\n"); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); + spin_unlock(&av7110->debilock); + wake_up(&cibuf->queue); + return; + } + + case DATA_MPEG_PLAY: + if (!av7110->playing) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + len = 0; + if (av7110->debitype & 0x100) { + spin_lock(&av7110->aout.lock); + len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); + spin_unlock(&av7110->aout.lock); + } + if (len <= 0 && (av7110->debitype & 0x200) + &&av7110->videostate.play_state != VIDEO_FREEZED) { + spin_lock(&av7110->avout.lock); + len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); + spin_unlock(&av7110->avout.lock); + } + if (len <= 0) { + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + break; + } + dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + dprintk(8, "DMA: MPEG_PLAY\n"); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_BMP_LOAD: + len = av7110->debilen; + dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); + if (!len) { + av7110->bmp_state = BMP_LOADED; + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); + iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); + wake_up(&av7110->bmpq); + dprintk(8, "gpio DATA_BMP_LOAD done\n"); + break; + } + if (len > av7110->bmplen) + len = av7110->bmplen; + if (len > 2 * 1024) + len = 2 * 1024; + iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); + iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); + memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); + av7110->bmpp += len; + av7110->bmplen -= len; + dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_CI_GET: + case DATA_COMMON_INTERFACE: + case DATA_FSECTION: + case DATA_IPMPE: + case DATA_PIPING: + if (!len || len > 4 * 1024) { + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + } + fallthrough; + + case DATA_TS_RECORD: + case DATA_PES_RECORD: + dprintk(8, "DMA: TS_REC etc.\n"); + start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_DEBUG_MESSAGE: + if (!len || len > 0xff) { + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + } + start_debi_dma(av7110, DEBI_READ, Reserved, len); + spin_unlock(&av7110->debilock); + return; + + case DATA_IRCOMMAND: +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) + av7110_ir_handler(av7110, + swahw32(irdebi(av7110, DEBINOSWAP, Reserved, + 0, 4))); +#endif + iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); + break; + + default: + printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", + av7110->debitype, av7110->debilen); + break; + } + av7110->debitype = -1; + ARM_ClearMailBox(av7110); + spin_unlock(&av7110->debilock); +} + + +#ifdef CONFIG_DVB_AV7110_OSD +static int dvb_osd_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(4, "%p\n", av7110); + + if (cmd == OSD_SEND_CMD) + return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); + if (cmd == OSD_GET_CAPABILITY) + return av7110_osd_capability(av7110, (osd_cap_t *) parg); + + return -EINVAL; +} + + +static const struct file_operations dvb_osd_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_osd = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_osd_fops, + .kernel_ioctl = dvb_osd_ioctl, +}; +#endif /* CONFIG_DVB_AV7110_OSD */ + + +static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid) +{ + u16 aflags = 0; + + dprintk(4, "%p\n", av7110); + + if (vpid == 0x1fff || apid == 0x1fff || + ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { + vpid = apid = ttpid = subpid = pcrpid = 0; + av7110->pids[DMX_PES_VIDEO] = 0; + av7110->pids[DMX_PES_AUDIO] = 0; + av7110->pids[DMX_PES_TELETEXT] = 0; + av7110->pids[DMX_PES_PCR] = 0; + } + + if (av7110->audiostate.bypass_mode) + aflags |= 0x8000; + + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, + pcrpid, vpid, apid, ttpid, subpid, aflags); +} + +int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid) +{ + int ret = 0; + dprintk(4, "%p\n", av7110); + + if (mutex_lock_interruptible(&av7110->pid_mutex)) + return -ERESTARTSYS; + + if (!(vpid & 0x8000)) + av7110->pids[DMX_PES_VIDEO] = vpid; + if (!(apid & 0x8000)) + av7110->pids[DMX_PES_AUDIO] = apid; + if (!(ttpid & 0x8000)) + av7110->pids[DMX_PES_TELETEXT] = ttpid; + if (!(pcrpid & 0x8000)) + av7110->pids[DMX_PES_PCR] = pcrpid; + + av7110->pids[DMX_PES_SUBTITLE] = 0; + + if (av7110->fe_synced) { + pcrpid = av7110->pids[DMX_PES_PCR]; + ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); + } + + mutex_unlock(&av7110->pid_mutex); + return ret; +} + + +/****************************************************************************** + * hardware filter functions + ******************************************************************************/ + +static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) +{ + struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; + struct av7110 *av7110 = dvbdmxfeed->demux->priv; + u16 buf[20]; + int ret, i; + u16 handle; +// u16 mode = 0x0320; + u16 mode = 0xb96a; + + dprintk(4, "%p\n", av7110); + + if (av7110->full_ts) + return 0; + + if (dvbdmxfilter->type == DMX_TYPE_SEC) { + if (hw_sections) { + buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | + dvbdmxfilter->maskandmode[0]; + for (i = 3; i < 18; i++) + buf[i + 4 - 2] = + (dvbdmxfilter->filter.filter_value[i] << 8) | + dvbdmxfilter->maskandmode[i]; + mode = 4; + } + } else if ((dvbdmxfeed->ts_type & TS_PACKET) && + !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { + av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); + } + + buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; + buf[1] = 16; + buf[2] = dvbdmxfeed->pid; + buf[3] = mode; + + ret = av7110_fw_request(av7110, buf, 20, &handle, 1); + if (ret != 0 || handle >= 32) { + printk(KERN_ERR "dvb-ttpci: %s error buf %04x %04x %04x %04x ret %d handle %04x\n", + __func__, buf[0], buf[1], buf[2], buf[3], + ret, handle); + dvbdmxfilter->hw_handle = 0xffff; + if (!ret) + ret = -1; + return ret; + } + + av7110->handle2filter[handle] = dvbdmxfilter; + dvbdmxfilter->hw_handle = handle; + + return ret; +} + +static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) +{ + struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; + u16 buf[3]; + u16 answ[2]; + int ret; + u16 handle; + + dprintk(4, "%p\n", av7110); + + if (av7110->full_ts) + return 0; + + handle = dvbdmxfilter->hw_handle; + if (handle >= 32) { + printk("%s tried to stop invalid filter %04x, filter type = %x\n", + __func__, handle, dvbdmxfilter->type); + return -EINVAL; + } + + av7110->handle2filter[handle] = NULL; + + buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; + buf[1] = 1; + buf[2] = handle; + ret = av7110_fw_request(av7110, buf, 3, answ, 2); + if (ret != 0 || answ[1] != handle) { + printk(KERN_ERR "dvb-ttpci: %s error cmd %04x %04x %04x ret %x resp %04x %04x pid %d\n", + __func__, buf[0], buf[1], buf[2], ret, + answ[0], answ[1], dvbdmxfilter->feed->pid); + if (!ret) + ret = -1; + } + return ret; +} + + +static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct av7110 *av7110 = dvbdmx->priv; + u16 *pid = dvbdmx->pids, npids[5]; + int i; + int ret = 0; + + dprintk(4, "%p\n", av7110); + + npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + i = dvbdmxfeed->pes_type; + npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { + npids[i] = 0; + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + if (!ret) + ret = StartHWFilter(dvbdmxfeed->filter); + return ret; + } + if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + if (ret) + return ret; + } + + if (dvbdmxfeed->pes_type < 2 && npids[0]) + if (av7110->fe_synced) + { + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + if (ret) + return ret; + } + + if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { + if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) + ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); + if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) + ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); + } + return ret; +} + +static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct av7110 *av7110 = dvbdmx->priv; + u16 *pid = dvbdmx->pids, npids[5]; + int i; + + int ret = 0; + + dprintk(4, "%p\n", av7110); + + if (dvbdmxfeed->pes_type <= 1) { + ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); + if (ret) + return ret; + if (!av7110->rec_mode) + dvbdmx->recording = 0; + if (!av7110->playing) + dvbdmx->playing = 0; + } + npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + i = dvbdmxfeed->pes_type; + switch (i) { + case 2: //teletext + if (dvbdmxfeed->ts_type & TS_PACKET) + ret = StopHWFilter(dvbdmxfeed->filter); + npids[2] = 0; + break; + case 0: + case 1: + case 4: + if (!pids_off) + return 0; + npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + break; + } + if (!ret) + ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); + return ret; +} + +static int av7110_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = demux->priv; + int ret = 0; + + dprintk(4, "%p\n", av7110); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (!av7110->full_ts && feed->pid > 0x1fff) + return -EINVAL; + + if (feed->type == DMX_TYPE_TS) { + if ((feed->ts_type & TS_DECODER) && + (feed->pes_type <= DMX_PES_PCR)) { + switch (demux->dmx.frontend->source) { + case DMX_MEMORY_FE: + if (feed->ts_type & TS_DECODER) + if (feed->pes_type < 2 && + !(demux->pids[0] & 0x8000) && + !(demux->pids[1] & 0x8000)) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + ret = av7110_av_start_play(av7110,RP_AV); + if (!ret) + demux->playing = 1; + } + break; + default: + ret = dvb_feed_start_pid(feed); + break; + } + } else if ((feed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE)) { + ret = StartHWFilter(feed->filter); + } + } + + if (av7110->full_ts) { + budget_start_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { + int i; + + for (i = 0; i < demux->filternum; i++) { + if (demux->filter[i].state != DMX_STATE_READY) + continue; + if (demux->filter[i].type != DMX_TYPE_SEC) + continue; + if (demux->filter[i].filter.parent != &feed->feed.sec) + continue; + demux->filter[i].state = DMX_STATE_GO; + if (demux->dmx.frontend->source != DMX_MEMORY_FE) { + ret = StartHWFilter(&demux->filter[i]); + if (ret) + break; + } + } + } + + return ret; +} + + +static int av7110_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = demux->priv; + int i, rc, ret = 0; + dprintk(4, "%p\n", av7110); + + if (feed->type == DMX_TYPE_TS) { + if (feed->ts_type & TS_DECODER) { + if (feed->pes_type >= DMX_PES_OTHER || + !demux->pesfilter[feed->pes_type]) + return -EINVAL; + demux->pids[feed->pes_type] |= 0x8000; + demux->pesfilter[feed->pes_type] = NULL; + } + if (feed->ts_type & TS_DECODER && + feed->pes_type < DMX_PES_OTHER) { + ret = dvb_feed_stop_pid(feed); + } else + if ((feed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE)) + ret = StopHWFilter(feed->filter); + } + + if (av7110->full_ts) { + budget_stop_feed(feed); + return ret; + } + + if (feed->type == DMX_TYPE_SEC) { + for (i = 0; ifilternum; i++) { + if (demux->filter[i].state == DMX_STATE_GO && + demux->filter[i].filter.parent == &feed->feed.sec) { + demux->filter[i].state = DMX_STATE_READY; + if (demux->dmx.frontend->source != DMX_MEMORY_FE) { + rc = StopHWFilter(&demux->filter[i]); + if (!ret) + ret = rc; + /* keep going, stop as many filters as possible */ + } + } + } + } + + return ret; +} + + +static void restart_feeds(struct av7110 *av7110) +{ + struct dvb_demux *dvbdmx = &av7110->demux; + struct dvb_demux_feed *feed; + int mode; + int feeding; + int i, j; + + dprintk(4, "%p\n", av7110); + + mode = av7110->playing; + av7110->playing = 0; + av7110->rec_mode = 0; + + feeding = av7110->feeding1; /* full_ts mod */ + + for (i = 0; i < dvbdmx->feednum; i++) { + feed = &dvbdmx->feed[i]; + if (feed->state == DMX_STATE_GO) { + if (feed->type == DMX_TYPE_SEC) { + for (j = 0; j < dvbdmx->filternum; j++) { + if (dvbdmx->filter[j].type != DMX_TYPE_SEC) + continue; + if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) + continue; + if (dvbdmx->filter[j].state == DMX_STATE_GO) + dvbdmx->filter[j].state = DMX_STATE_READY; + } + } + av7110_start_feed(feed); + } + } + + av7110->feeding1 = feeding; /* full_ts mod */ + + if (mode) + av7110_av_start_play(av7110, mode); +} + +static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, + uint64_t *stc, unsigned int *base) +{ + int ret; + u16 fwstc[4]; + u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); + struct dvb_demux *dvbdemux; + struct av7110 *av7110; + + /* pointer casting paranoia... */ + BUG_ON(!demux); + dvbdemux = demux->priv; + BUG_ON(!dvbdemux); + av7110 = dvbdemux->priv; + + dprintk(4, "%p\n", av7110); + + if (num != 0) + return -EINVAL; + + ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); + if (ret) { + printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); + return ret; + } + dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", + fwstc[0], fwstc[1], fwstc[2], fwstc[3]); + + *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | + (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); + *base = 1; + + dprintk(4, "stc = %lu\n", (unsigned long)*stc); + + return 0; +} + + +/****************************************************************************** + * SEC device file operations + ******************************************************************************/ + + +static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) +{ + struct av7110* av7110 = fe->dvb->priv; + + switch (tone) { + case SEC_TONE_ON: + return Set22K(av7110, 1); + + case SEC_TONE_OFF: + return Set22K(av7110, 0); + + default: + return -EINVAL; + } +} + +static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); +} + +static int av7110_diseqc_send_burst(struct dvb_frontend* fe, + enum fe_sec_mini_cmd minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + return av7110_diseqc_send(av7110, 0, NULL, minicmd); +} + +/* simplified code from budget-core.c */ +static int stop_ts_capture(struct av7110 *budget) +{ + dprintk(2, "budget: %p\n", budget); + + if (--budget->feeding1) + return budget->feeding1; + saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ + SAA7146_IER_DISABLE(budget->dev, MASK_10); + SAA7146_ISR_CLEAR(budget->dev, MASK_10); + return 0; +} + +static int start_ts_capture(struct av7110 *budget) +{ + unsigned y; + + dprintk(2, "budget: %p\n", budget); + + if (budget->feeding1) + return ++budget->feeding1; + for (y = 0; y < TS_HEIGHT; y++) + memset(budget->grabbing + y * TS_WIDTH, 0x00, TS_WIDTH); + budget->ttbp = 0; + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ + SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ + saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ + return ++budget->feeding1; +} + +static int budget_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *budget = demux->priv; + int status; + + dprintk(2, "av7110: %p\n", budget); + + spin_lock(&budget->feedlock1); + feed->pusi_seen = false; /* have a clean section start */ + status = start_ts_capture(budget); + spin_unlock(&budget->feedlock1); + return status; +} + +static int budget_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *budget = demux->priv; + int status; + + dprintk(2, "budget: %p\n", budget); + + spin_lock(&budget->feedlock1); + status = stop_ts_capture(budget); + spin_unlock(&budget->feedlock1); + return status; +} + +static void vpeirq(struct tasklet_struct *t) +{ + struct av7110 *budget = from_tasklet(budget, t, vpe_tasklet); + u8 *mem = (u8 *) (budget->grabbing); + u32 olddma = budget->ttbp; + u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; + + /* nearest lower position divisible by 188 */ + newdma -= newdma % 188; + + if (newdma >= TS_BUFLEN) + return; + + budget->ttbp = newdma; + + if (!budget->feeding1 || (newdma == olddma)) + return; + + /* Ensure streamed PCI data is synced to CPU */ + dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, + budget->pt.nents, DMA_FROM_DEVICE); + +#if 0 + /* track rps1 activity */ + printk("vpeirq: %02x Event Counter 1 0x%04x\n", + mem[olddma], + saa7146_read(budget->dev, EC1R) & 0x3fff); +#endif + + if (newdma > olddma) + /* no wraparound, dump olddma..newdma */ + dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); + else { + /* wraparound, dump olddma..buflen and 0..newdma */ + dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + dvb_dmx_swfilter_packets(demux, mem, newdma / 188); + } +} + +static int av7110_register(struct av7110 *av7110) +{ + int ret, i; + struct dvb_demux *dvbdemux = &av7110->demux; + struct dvb_demux *dvbdemux1 = &av7110->demux1; + + dprintk(4, "%p\n", av7110); + + if (av7110->registered) + return -1; + + av7110->registered = 1; + + dvbdemux->priv = (void *) av7110; + + for (i = 0; i < 32; i++) + av7110->handle2filter[i] = NULL; + + dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; + dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; + dvbdemux->start_feed = av7110_start_feed; + dvbdemux->stop_feed = av7110_stop_feed; + dvbdemux->write_to_decoder = av7110_write_to_decoder; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&av7110->demux); + av7110->demux.dmx.get_stc = dvb_get_stc; + + av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; + av7110->dmxdev.demux = &dvbdemux->dmx; + av7110->dmxdev.capabilities = 0; + + dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); + + av7110->hw_frontend.source = DMX_FRONTEND_0; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); + + if (ret < 0) + return ret; + + av7110->mem_frontend.source = DMX_MEMORY_FE; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); + + if (ret < 0) + return ret; + + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, + &av7110->hw_frontend); + if (ret < 0) + return ret; + + av7110_av_register(av7110); + av7110_ca_register(av7110); + +#ifdef CONFIG_DVB_AV7110_OSD + dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, + &dvbdev_osd, av7110, DVB_DEVICE_OSD, 0); +#endif + + dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); + + if (budgetpatch) { + /* initialize software demux1 without its own frontend + * demux1 hardware is connected to frontend0 of demux0 + */ + dvbdemux1->priv = (void *) av7110; + + dvbdemux1->filternum = 256; + dvbdemux1->feednum = 256; + dvbdemux1->start_feed = budget_start_feed; + dvbdemux1->stop_feed = budget_stop_feed; + dvbdemux1->write_to_decoder = NULL; + + dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&av7110->demux1); + + av7110->dmxdev1.filternum = 256; + av7110->dmxdev1.demux = &dvbdemux1->dmx; + av7110->dmxdev1.capabilities = 0; + + dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); + + dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); + printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); + } + return 0; +} + + +static void dvb_unregister(struct av7110 *av7110) +{ + struct dvb_demux *dvbdemux = &av7110->demux; + struct dvb_demux *dvbdemux1 = &av7110->demux1; + + dprintk(4, "%p\n", av7110); + + if (!av7110->registered) + return; + + if (budgetpatch) { + dvb_net_release(&av7110->dvb_net1); + dvbdemux->dmx.close(&dvbdemux1->dmx); + dvb_dmxdev_release(&av7110->dmxdev1); + dvb_dmx_release(&av7110->demux1); + } + + dvb_net_release(&av7110->dvb_net); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); + + dvb_dmxdev_release(&av7110->dmxdev); + dvb_dmx_release(&av7110->demux); + + if (av7110->fe != NULL) { + dvb_unregister_frontend(av7110->fe); + dvb_frontend_detach(av7110->fe); + } + dvb_unregister_device(av7110->osd_dev); + av7110_av_unregister(av7110); + av7110_ca_unregister(av7110); +} + + +/**************************************************************************** + * I2C client commands + ****************************************************************************/ + +int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) +{ + u8 msg[2] = { reg, val }; + struct i2c_msg msgs; + + msgs.flags = 0; + msgs.addr = id / 2; + msgs.len = 2; + msgs.buf = msg; + return i2c_transfer(&av7110->i2c_adap, &msgs, 1); +} + +u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) +{ + u8 mm1[] = {0x00}; + u8 mm2[] = {0x00}; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = id / 2; + mm1[0] = reg; + msgs[0].len = 1; msgs[1].len = 1; + msgs[0].buf = mm1; msgs[1].buf = mm2; + i2c_transfer(&av7110->i2c_adap, msgs, 2); + + return mm2[0]; +} + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + + +static int check_firmware(struct av7110* av7110) +{ + u32 crc = 0, len = 0; + unsigned char *ptr; + + /* check for firmware magic */ + ptr = av7110->bin_fw; + if (ptr[0] != 'A' || ptr[1] != 'V' || + ptr[2] != 'F' || ptr[3] != 'W') { + printk("dvb-ttpci: this is not an av7110 firmware\n"); + return -EINVAL; + } + ptr += 4; + + /* check dpram file */ + crc = get_unaligned_be32(ptr); + ptr += 4; + len = get_unaligned_be32(ptr); + ptr += 4; + if (len >= 512) { + printk("dvb-ttpci: dpram file is way too big.\n"); + return -EINVAL; + } + if (crc != crc32_le(0, ptr, len)) { + printk("dvb-ttpci: crc32 of dpram file does not match.\n"); + return -EINVAL; + } + av7110->bin_dpram = ptr; + av7110->size_dpram = len; + ptr += len; + + /* check root file */ + crc = get_unaligned_be32(ptr); + ptr += 4; + len = get_unaligned_be32(ptr); + ptr += 4; + + if (len <= 200000 || len >= 300000 || + len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { + printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); + return -EINVAL; + } + if( crc != crc32_le(0, ptr, len)) { + printk("dvb-ttpci: crc32 of root file does not match.\n"); + return -EINVAL; + } + av7110->bin_root = ptr; + av7110->size_root = len; + return 0; +} + +static void put_firmware(struct av7110* av7110) +{ + vfree(av7110->bin_fw); +} + +static int get_firmware(struct av7110* av7110) +{ + int ret; + const struct firmware *fw; + + /* request the av7110 firmware, this will block until someone uploads it */ + ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); + if (ret) { + if (ret == -ENOENT) { + printk(KERN_ERR "dvb-ttpci: could not load firmware, file not found: dvb-ttpci-01.fw\n"); + printk(KERN_ERR "dvb-ttpci: usually this should be in /usr/lib/hotplug/firmware or /lib/firmware\n"); + printk(KERN_ERR "dvb-ttpci: and can be downloaded from https://linuxtv.org/download/dvb/firmware/\n"); + } else + printk(KERN_ERR "dvb-ttpci: cannot request firmware (error %i)\n", + ret); + return -EINVAL; + } + + if (fw->size <= 200000) { + printk("dvb-ttpci: this firmware is way too small.\n"); + release_firmware(fw); + return -EINVAL; + } + + /* check if the firmware is available */ + av7110->bin_fw = vmalloc(fw->size); + if (NULL == av7110->bin_fw) { + dprintk(1, "out of memory\n"); + release_firmware(fw); + return -ENOMEM; + } + + memcpy(av7110->bin_fw, fw->data, fw->size); + av7110->size_fw = fw->size; + if ((ret = check_firmware(av7110))) + vfree(av7110->bin_fw); + + release_firmware(fw); + return ret; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (p->frequency + 479500) / 125; + + if (p->frequency > 2000000) + pwr = 3; + else if (p->frequency > 1800000) + pwr = 2; + else if (p->frequency > 1600000) + pwr = 1; + else if (p->frequency > 1200000) + pwr = 0; + else if (p->frequency >= 1100000) + pwr = 1; + else + pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (p->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + + + + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = p->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + + + +static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u32 f = p->frequency; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (f + 36125000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1820_config philips_cd1516_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + + + +static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div, pwr; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (p->frequency + 36200000) / 166666; + + if (p->frequency <= 782000000) + pwr = 1; + else + pwr = 2; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85; + data[3] = pwr << 6; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +{ +#if IS_ENABLED(CONFIG_DVB_SP8870) + struct av7110* av7110 = fe->dvb->priv; + + return request_firmware(fw, name, &av7110->dev->pci->dev); +#else + return -EINVAL; +#endif +} + +static const struct sp8870_config alps_tdlb7_config = { + + .demod_address = 0x71, + .request_firmware = alps_tdlb7_request_firmware, +}; + + +static u8 nexusca_stv0297_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0x7b, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x49, + 0x62, 0x0b, + 0x53, 0x08, + 0x59, 0x08, + 0xff, 0xff, +}; + +static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; + int i; + + div = (p->frequency + 36150000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (p->frequency < 45000000) + return -EINVAL; + else if (p->frequency < 137000000) + data[3] = 0x01; + else if (p->frequency < 403000000) + data[3] = 0x02; + else if (p->frequency < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { + printk("nexusca: pll transfer failed!\n"); + return -EIO; + } + + // wait for PLL lock + for(i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) + if (data[0] & 0x40) break; + msleep(10); + } + + return 0; +} + +static struct stv0297_config nexusca_stv0297_config = { + + .demod_address = 0x1C, + .inittab = nexusca_stv0297_inittab, + .invert = 1, + .stop_during_read = 1, +}; + + + +static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct av7110* av7110 = fe->dvb->priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (36125000 + p->frequency) / 166666; + + cfg = 0x88; + + if (p->frequency < 175000000) + cpump = 2; + else if (p->frequency < 390000000) + cpump = 1; + else if (p->frequency < 470000000) + cpump = 2; + else if (p->frequency < 750000000) + cpump = 1; + else + cpump = 3; + + if (p->frequency < 175000000) + band_select = 0x0e; + else if (p->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, +}; + + + +static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) +{ + int ret = 0; + int synced = (status & FE_HAS_LOCK) ? 1 : 0; + + av7110->fe_status = status; + + if (av7110->fe_synced == synced) + return 0; + + if (av7110->playing) { + av7110->fe_synced = synced; + return 0; + } + + if (mutex_lock_interruptible(&av7110->pid_mutex)) + return -ERESTARTSYS; + + if (synced) { + ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], 0, + av7110->pids[DMX_PES_PCR]); + if (!ret) + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + } else { + ret = SetPIDs(av7110, 0, 0, 0, 0, 0); + if (!ret) { + ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + if (!ret) + ret = av7110_wait_msgstate(av7110, GPMQBusy); + } + } + + if (!ret) + av7110->fe_synced = synced; + + mutex_unlock(&av7110->pid_mutex); + return ret; +} + +static int av7110_fe_set_frontend(struct dvb_frontend *fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_set_frontend(fe); + + return ret; +} + +static int av7110_fe_init(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_init(fe); + return ret; +} + +static int av7110_fe_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct av7110* av7110 = fe->dvb->priv; + + /* call the real implementation */ + int ret = av7110->fe_read_status(fe, status); + if (!ret) + if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) + ret = av7110_fe_lock_fix(av7110, *status); + return ret; +} + +static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_diseqc_reset_overload(fe); + return ret; +} + +static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd* cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_master_cmd = *cmd; + ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); + } + return ret; +} + +static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_minicmd = minicmd; + ret = av7110->fe_diseqc_send_burst(fe, minicmd); + } + return ret; +} + +static int av7110_fe_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_tone = tone; + ret = av7110->fe_set_tone(fe, tone); + } + return ret; +} + +static int av7110_fe_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { + av7110->saved_voltage = voltage; + ret = av7110->fe_set_voltage(fe, voltage); + } + return ret; +} + +static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) +{ + struct av7110* av7110 = fe->dvb->priv; + + int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) + ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); + return ret; +} + +static void dvb_s_recover(struct av7110* av7110) +{ + av7110_fe_init(av7110->fe); + + av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); + if (av7110->saved_master_cmd.msg_len) { + msleep(20); + av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); + } + msleep(20); + av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); + msleep(20); + av7110_fe_set_tone(av7110->fe, av7110->saved_tone); + + av7110_fe_set_frontend(av7110->fe); +} + +static u8 read_pwm(struct av7110* av7110) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static int frontend_init(struct av7110 *av7110) +{ + int ret; + + if (av7110->dev->pci->subsystem_vendor == 0x110a) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, + &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; + } + break; + } + + } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X + case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE + + // try the ALPS BSRV2 first of all + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + // try the ALPS BSRU6 now + av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + // Try the grundig 29504-451 + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + break; + } + + /* Try DVB-C cards */ + switch(av7110->dev->pci->subsystem_device) { + case 0x0000: + /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ + av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, + read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; + } + break; + case 0x0003: + /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, + read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } + break; + } + break; + + case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X + { + struct dvb_frontend *fe; + + // try ALPS TDLB7 first, then Grundig 29504-401 + fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; + av7110->fe = fe; + break; + } + } + fallthrough; + + case 0x0008: // Hauppauge/TT DVB-T + // Grundig 29504-401 + av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); + if (av7110->fe) + av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + break; + + case 0x0002: // Hauppauge/TT DVB-C premium rev2.X + + av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } + break; + + case 0x0004: // Galaxis DVB-S rev1.3 + /* ALPS BSRV2 */ + av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + } + break; + + case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ + /* Grundig 29504-451 */ + av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; + av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; + av7110->fe->ops.set_tone = av7110_set_tone; + av7110->recover = dvb_s_recover; + } + break; + + case 0x000A: // Hauppauge/TT Nexus-CA rev1.X + + av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; + + /* set TDA9819 into DVB mode */ + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + + /* tuner on this needs a slower i2c bus speed */ + av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + break; + } + break; + + case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ + /* ALPS BSBE1 */ + av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); + if (av7110->fe) { + av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + + if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { + printk("dvb-ttpci: LNBP21 not found!\n"); + if (av7110->fe->ops.release) + av7110->fe->ops.release(av7110->fe); + av7110->fe = NULL; + } else { + av7110->fe->ops.dishnetwork_send_legacy_command = NULL; + av7110->recover = dvb_s_recover; + } + } + break; + } + } + + if (!av7110->fe) { + /* FIXME: propagate the failure code from the lower layers */ + ret = -ENOMEM; + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + av7110->dev->pci->vendor, + av7110->dev->pci->device, + av7110->dev->pci->subsystem_vendor, + av7110->dev->pci->subsystem_device); + } else { + FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); + FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); + FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); + FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); + FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); + + ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); + if (ret < 0) { + printk("av7110: Frontend registration failed!\n"); + dvb_frontend_detach(av7110->fe); + av7110->fe = NULL; + } + } + return ret; +} + +/* Budgetpatch note: + * Original hardware design by Roberto Deza: + * There is a DVB_Wiki at + * https://linuxtv.org + * + * New software triggering design by Emard that works on + * original Roberto Deza's hardware: + * + * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. + * GPIO3 is in budget-patch hardware connectd to port B VSYNC + * HS is an internal event of 7146, accessible with RPS + * and temporarily raised high every n lines + * (n in defined in the RPS_THRESH1 counter threshold) + * I think HS is raised high on the beginning of the n-th line + * and remains high until this n-th line that triggered + * it is completely received. When the reception of n-th line + * ends, HS is lowered. + * + * To transmit data over DMA, 7146 needs changing state at + * port B VSYNC pin. Any changing of port B VSYNC will + * cause some DMA data transfer, with more or less packets loss. + * It depends on the phase and frequency of VSYNC and + * the way of 7146 is instructed to trigger on port B (defined + * in DD1_INIT register, 3rd nibble from the right valid + * numbers are 0-7, see datasheet) + * + * The correct triggering can minimize packet loss, + * dvbtraffic should give this stable bandwidths: + * 22k transponder = 33814 kbit/s + * 27.5k transponder = 38045 kbit/s + * by experiment it is found that the best results + * (stable bandwidths and almost no packet loss) + * are obtained using DD1_INIT triggering number 2 + * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) + * and a VSYNC phase that occurs in the middle of DMA transfer + * (about byte 188*512=96256 in the DMA window). + * + * Phase of HS is still not clear to me how to control, + * It just happens to be so. It can be seen if one enables + * RPS_IRQ and print Event Counter 1 in vpeirq(). Every + * time RPS_INTERRUPT is called, the Event Counter 1 will + * increment. That's how the 7146 is programmed to do event + * counting in this budget-patch.c + * I *think* HPS setting has something to do with the phase + * of HS but I can't be 100% sure in that. + * + * hardware debug note: a working budget card (including budget patch) + * with vpeirq() interrupt setup in mode "0x90" (every 64K) will + * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes + * and that means 3*25=75 Hz of interrupt frequency, as seen by + * watch cat /proc/interrupts + * + * If this frequency is 3x lower (and data received in the DMA + * buffer don't start with 0x47, but in the middle of packets, + * whose lengths appear to be like 188 292 188 104 etc. + * this means VSYNC line is not connected in the hardware. + * (check soldering pcb and pins) + * The same behaviour of missing VSYNC can be duplicated on budget + * cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. + */ +static int av7110_attach(struct saa7146_dev* dev, + struct saa7146_pci_extension_data *pci_ext) +{ + const int length = TS_WIDTH * TS_HEIGHT; + struct pci_dev *pdev = dev->pci; + struct av7110 *av7110; + struct task_struct *thread; + int ret, count = 0; + + dprintk(4, "dev: %p\n", dev); + + /* Set RPS_IRQ to 1 to track rps1 activity. + * Enabling this won't send any interrupt to PC CPU. + */ +#define RPS_IRQ 0 + + if (budgetpatch == 1) { + budgetpatch = 0; + /* autodetect the presence of budget patch + * this only works if saa7146 has been recently + * reset with MASK_31 to MC1 + * + * will wait for VBI_B event (vertical blank at port B) + * and will reset GPIO3 after VBI_B is detected. + * (GPIO3 should be raised high by CPU to + * test if GPIO3 will generate vertical blank signal + * in budget patch GPIO3 is connected to VSYNC_B + */ + + /* RESET SAA7146 */ + saa7146_write(dev, MC1, MASK_31); + /* autodetection success seems to be time-dependend after reset */ + + /* Fix VSYNC level */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + /* set vsync_b triggering */ + saa7146_write(dev, DD1_STREAM_B, 0); + /* port B VSYNC at rising edge */ + saa7146_write(dev, DD1_INIT, 0x00000200); + saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI + saa7146_write(dev, MC2, + 1 * (MASK_08 | MASK_24) | // BRS control + 0 * (MASK_09 | MASK_25) | // a + 1 * (MASK_10 | MASK_26) | // b + 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 + 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 + 0 * (MASK_01 | MASK_15) // DEBI + ); + + /* start writing RPS1 code from beginning */ + count = 0; + /* Disable RPS1 */ + saa7146_write(dev, MC1, MASK_29); + /* RPS1 timeout disable */ + saa7146_write(dev, RPS_TOV1, 0); + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + /* issue RPS1 interrupt to increment counter */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + WRITE_RPS1(CMD_STOP); + /* Jump to begin of RPS program as safety measure (p37) */ + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + +#if RPS_IRQ + /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + */ + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + /* Set RPS1 Address register to point to RPS code (r108 p42) */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + /* Enable RPS1, (rFC p33) */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); + + mdelay(10); + /* now send VSYNC_B to rps1 by rising GPIO3 */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + mdelay(10); + /* if rps1 responded by lowering the GPIO3, + * then we have budgetpatch hardware + */ + if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { + budgetpatch = 1; + printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); + } + /* Disable RPS1 */ + saa7146_write(dev, MC1, ( MASK_29 )); +#if RPS_IRQ + printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +#endif + } + + /* prepare the av7110 device struct */ + av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); + if (!av7110) { + dprintk(1, "out of memory\n"); + return -ENOMEM; + } + + av7110->card_name = (char*) pci_ext->ext_priv; + av7110->dev = dev; + dev->ext_priv = av7110; + + ret = get_firmware(av7110); + if (ret < 0) + goto err_kfree_0; + + ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, + THIS_MODULE, &dev->pci->dev, adapter_nr); + if (ret < 0) + goto err_put_firmware_1; + + /* the Siemens DVB needs this if you want to have the i2c chips + get recognized before the main driver is fully loaded */ + saa7146_write(dev, GPIO_CTRL, 0x500000); + + strscpy(av7110->i2c_adap.name, pci_ext->ext_priv, + sizeof(av7110->i2c_adap.name)); + + saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ + + ret = i2c_add_adapter(&av7110->i2c_adap); + if (ret < 0) + goto err_dvb_unregister_adapter_2; + + ttpci_eeprom_parse_mac(&av7110->i2c_adap, + av7110->dvb_adapter.proposed_mac); + ret = -ENOMEM; + + /* full-ts mod? */ + if (full_ts) + av7110->full_ts = true; + + /* check for full-ts flag in eeprom */ + if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { + u8 flags = i2c_readreg(av7110, 0xaa, 2); + if (flags != 0xff && (flags & 0x01)) + av7110->full_ts = true; + } + + if (av7110->full_ts) { + printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); + spin_lock_init(&av7110->feedlock1); + av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, + &av7110->pt); + if (!av7110->grabbing) + goto err_i2c_del_3; + + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + saa7146_write(dev, BRS_CTRL, 0x60000000); + saa7146_write(dev, MC2, MASK_08 | MASK_24); + + /* dma3 */ + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + saa7146_write(dev, BASE_ODD3, 0); + saa7146_write(dev, BASE_EVEN3, 0); + saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, PITCH3, TS_WIDTH); + saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); + saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); + saa7146_write(dev, MC2, MASK_04 | MASK_20); + + tasklet_setup(&av7110->vpe_tasklet, vpeirq); + + } else if (budgetpatch) { + spin_lock_init(&av7110->feedlock1); + av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, + &av7110->pt); + if (!av7110->grabbing) + goto err_i2c_del_3; + + saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); + saa7146_write(dev, BCS_CTRL, 0x80400040); + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x03000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + saa7146_write(dev, BASE_ODD3, 0); + saa7146_write(dev, BASE_EVEN3, 0); + saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); + + saa7146_write(dev, PITCH3, TS_WIDTH); + saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); + + /* upload all */ + saa7146_write(dev, MC2, 0x077c077c); + saa7146_write(dev, GPIO_CTRL, 0x000000); +#if RPS_IRQ + /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + */ + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + /* set event counter 1 threshold to maximum allowed value (rEC p55) */ + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ + count = 0; + + /* Wait Source Line Counter Threshold (p36) */ + WRITE_RPS1(CMD_PAUSE | EVT_HS); + /* Set GPIO3=1 (p42) */ + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +#if RPS_IRQ + /* issue RPS1 interrupt */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + /* Wait reset Source Line Counter Threshold (p36) */ + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); + /* Set GPIO3=0 (p42) */ + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + /* issue RPS1 interrupt */ + WRITE_RPS1(CMD_INTERRUPT); +#endif + /* Jump to begin of RPS program (p37) */ + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + + /* Fix VSYNC level */ + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + /* Set RPS1 Address register to point to RPS code (r108 p42) */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + /* Set Source Line Counter Threshold, using BRS (rCC p43) + * It generates HS event every TS_HEIGHT lines + * this is related to TS_WIDTH set in register + * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits + * are set to TS_WIDTH bytes (TS_WIDTH=2*188), + * then RPS_THRESH1 should be set to trigger + * every TS_HEIGHT (512) lines. + */ + saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); + + /* Enable RPS1 (rFC p33) */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + /* end of budgetpatch register initialization */ + tasklet_setup(&av7110->vpe_tasklet, vpeirq); + } else { + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + saa7146_write(dev, BCS_CTRL, 0x80400040); + + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x03000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* upload all */ + saa7146_write(dev, MC2, 0x077c077c); + saa7146_write(dev, GPIO_CTRL, 0x000000); + } + + tasklet_setup(&av7110->debi_tasklet, debiirq); + tasklet_setup(&av7110->gpio_tasklet, gpioirq); + + mutex_init(&av7110->pid_mutex); + + /* locks for data transfers from/to AV7110 */ + spin_lock_init(&av7110->debilock); + mutex_init(&av7110->dcomlock); + av7110->debitype = -1; + + /* default OSD window */ + av7110->osdwin = 1; + mutex_init(&av7110->osd_mutex); + + /* TV standard */ + av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC + : AV7110_VIDEO_MODE_PAL; + + /* ARM "watchdog" */ + init_waitqueue_head(&av7110->arm_wait); + av7110->arm_thread = NULL; + + /* allocate and init buffers */ + av7110->debi_virt = dma_alloc_coherent(&pdev->dev, 8192, + &av7110->debi_bus, GFP_KERNEL); + if (!av7110->debi_virt) + goto err_saa71466_vfree_4; + + + av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); + if (!av7110->iobuf) + goto err_pci_free_5; + + ret = av7110_av_init(av7110); + if (ret < 0) + goto err_iobuf_vfree_6; + + /* init BMP buffer */ + av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; + init_waitqueue_head(&av7110->bmpq); + + ret = av7110_ca_init(av7110); + if (ret < 0) + goto err_av7110_av_exit_7; + + /* load firmware into AV7110 cards */ + ret = av7110_bootarm(av7110); + if (ret < 0) + goto err_av7110_ca_exit_8; + + ret = av7110_firmversion(av7110); + if (ret < 0) + goto err_stop_arm_9; + + if (FW_VERSION(av7110->arm_app)<0x2501) + printk(KERN_WARNING + "dvb-ttpci: Warning, firmware version 0x%04x is too old. System might be unstable!\n", + FW_VERSION(av7110->arm_app)); + + thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); + if (IS_ERR(thread)) { + ret = PTR_ERR(thread); + goto err_stop_arm_9; + } + av7110->arm_thread = thread; + + /* set initial volume in mixer struct */ + av7110->mixer.volume_left = volume; + av7110->mixer.volume_right = volume; + + ret = av7110_register(av7110); + if (ret < 0) + goto err_arm_thread_stop_10; + + init_av7110_av(av7110); + + /* special case DVB-C: these cards have an analog tuner + plus need some special handling, so we have separate + saa7146_ext_vv data for these... */ + ret = av7110_init_v4l(av7110); + if (ret < 0) + goto err_av7110_unregister_11; + + av7110->dvb_adapter.priv = av7110; + ret = frontend_init(av7110); + if (ret < 0) + goto err_av7110_exit_v4l_12; + + mutex_init(&av7110->ioctl_mutex); + +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) + av7110_ir_init(av7110); +#endif + printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); + av7110_num++; +out: + return ret; + +err_av7110_exit_v4l_12: + av7110_exit_v4l(av7110); +err_av7110_unregister_11: + dvb_unregister(av7110); +err_arm_thread_stop_10: + av7110_arm_sync(av7110); +err_stop_arm_9: + /* Nothing to do. Rejoice. */ +err_av7110_ca_exit_8: + av7110_ca_exit(av7110); +err_av7110_av_exit_7: + av7110_av_exit(av7110); +err_iobuf_vfree_6: + vfree(av7110->iobuf); +err_pci_free_5: + dma_free_coherent(&pdev->dev, 8192, av7110->debi_virt, + av7110->debi_bus); +err_saa71466_vfree_4: + if (av7110->grabbing) + saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); +err_i2c_del_3: + i2c_del_adapter(&av7110->i2c_adap); +err_dvb_unregister_adapter_2: + dvb_unregister_adapter(&av7110->dvb_adapter); +err_put_firmware_1: + put_firmware(av7110); +err_kfree_0: + kfree(av7110); + goto out; +} + +static int av7110_detach(struct saa7146_dev* saa) +{ + struct av7110 *av7110 = saa->ext_priv; + dprintk(4, "%p\n", av7110); + +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) + av7110_ir_exit(av7110); +#endif + if (budgetpatch || av7110->full_ts) { + if (budgetpatch) { + /* Disable RPS1 */ + saa7146_write(saa, MC1, MASK_29); + /* VSYNC LOW (inactive) */ + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } + saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ + SAA7146_IER_DISABLE(saa, MASK_10); + SAA7146_ISR_CLEAR(saa, MASK_10); + msleep(50); + tasklet_kill(&av7110->vpe_tasklet); + saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); + } + av7110_exit_v4l(av7110); + + av7110_arm_sync(av7110); + + tasklet_kill(&av7110->debi_tasklet); + tasklet_kill(&av7110->gpio_tasklet); + + dvb_unregister(av7110); + + SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); + + av7110_ca_exit(av7110); + av7110_av_exit(av7110); + + vfree(av7110->iobuf); + dma_free_coherent(&saa->pci->dev, 8192, av7110->debi_virt, + av7110->debi_bus); + + i2c_del_adapter(&av7110->i2c_adap); + + dvb_unregister_adapter (&av7110->dvb_adapter); + + av7110_num--; + + put_firmware(av7110); + + kfree(av7110); + + saa->ext_priv = NULL; + + return 0; +} + + +static void av7110_irq(struct saa7146_dev* dev, u32 *isr) +{ + struct av7110 *av7110 = dev->ext_priv; + + //print_time("av7110_irq"); + + /* Note: Don't try to handle the DEBI error irq (MASK_18), in + * intel mode the timeout is asserted all the time... + */ + + if (*isr & MASK_19) { + //printk("av7110_irq: DEBI\n"); + /* Note 1: The DEBI irq is level triggered: We must enable it + * only after we started a DMA xfer, and disable it here + * immediately, or it will be signalled all the time while + * DEBI is idle. + * Note 2: You would think that an irq which is masked is + * not signalled by the hardware. Not so for the SAA7146: + * An irq is signalled as long as the corresponding bit + * in the ISR is set, and disabling irqs just prevents the + * hardware from setting the ISR bit. This means a) that we + * must clear the ISR *after* disabling the irq (which is why + * we must do it here even though saa7146_core did it already), + * and b) that if we were to disable an edge triggered irq + * (like the gpio irqs sadly are) temporarily we would likely + * loose some. This sucks :-( + */ + SAA7146_IER_DISABLE(av7110->dev, MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19); + tasklet_schedule(&av7110->debi_tasklet); + } + + if (*isr & MASK_03) { + //printk("av7110_irq: GPIO\n"); + tasklet_schedule(&av7110->gpio_tasklet); + } + + if (*isr & MASK_10) + tasklet_schedule(&av7110->vpe_tasklet); +} + + +static struct saa7146_extension av7110_extension_driver; + +#define MAKE_AV7110_INFO(x_var,x_name) \ +static struct saa7146_pci_extension_data x_var = { \ + .ext_priv = x_name, \ + .ext = &av7110_extension_driver } + +MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); +MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); +MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); +MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); +MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); +MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); +MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); +MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); +MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); +MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); +MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), + MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), + MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), + MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), + MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), + MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), + MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), + MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), + MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), + MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), + MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), + +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 +/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? + + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + + +static struct saa7146_extension av7110_extension_driver = { + .name = "av7110", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = &pci_tbl[0], + .attach = av7110_attach, + .detach = av7110_detach, + + .irq_mask = MASK_19 | MASK_03 | MASK_10, + .irq_func = av7110_irq, +}; + + +static int __init av7110_init(void) +{ + return saa7146_register_extension(&av7110_extension_driver); +} + + +static void __exit av7110_exit(void) +{ + saa7146_unregister_extension(&av7110_extension_driver); +} + +module_init(av7110_init); +module_exit(av7110_exit); + +MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by Siemens, Technotrend, Hauppauge"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110.h new file mode 100644 index 000000000000..9fde69b38f1c --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110.h @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AV7110_H_ +#define _AV7110_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "dvb_filter.h" +#include +#include +#include +#include "ves1820.h" +#include "ves1x93.h" +#include "stv0299.h" +#include "tda8083.h" +#include "sp8870.h" +#include "stv0297.h" +#include "l64781.h" + +#include "saa7146_vv.h" + + +#define ANALOG_TUNER_VES1820 1 +#define ANALOG_TUNER_STV0297 2 + +extern int av7110_debug; + +#define dprintk(level, fmt, arg...) do { \ + if (level & av7110_debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ + __func__, ##arg); \ +} while (0) + +#define MAXFILT 32 + +enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; + +enum av7110_video_mode { + AV7110_VIDEO_MODE_PAL = 0, + AV7110_VIDEO_MODE_NTSC = 1 +}; + +struct av7110_p2t { + u8 pes[TS_SIZE]; + u8 counter; + long int pos; + int frags; + struct dvb_demux_feed *feed; +}; + +/* video MPEG decoder events: */ +/* (code copied from dvb_frontend.c, should maybe be factored out...) */ +#define MAX_VIDEO_EVENT 8 +struct dvb_video_events { + struct video_event events[MAX_VIDEO_EVENT]; + int eventw; + int eventr; + int overflow; + wait_queue_head_t wait_queue; + spinlock_t lock; +}; + + +struct av7110; + +/* infrared remote control */ +struct infrared { + struct rc_dev *rcdev; + char input_phys[32]; + u32 ir_config; +}; + +/* place to store all the necessary device information */ +struct av7110 { + + /* devices */ + + struct dvb_device dvb_dev; + struct dvb_net dvb_net; + + struct video_device v4l_dev; + struct video_device vbi_dev; + + struct saa7146_dev *dev; + + struct i2c_adapter i2c_adap; + + char *card_name; + + /* support for analog module of dvb-c */ + int analog_tuner_flags; + int current_input; + u32 current_freq; + + struct tasklet_struct debi_tasklet; + struct tasklet_struct gpio_tasklet; + + int adac_type; /* audio DAC type */ +#define DVB_ADAC_TI 0 +#define DVB_ADAC_CRYSTAL 1 +#define DVB_ADAC_MSP34x0 2 +#define DVB_ADAC_MSP34x5 3 +#define DVB_ADAC_NONE -1 + + + /* buffers */ + + void *iobuf; /* memory for all buffers */ + struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ +#define AVOUTLEN (128*1024) + struct dvb_ringbuffer aout; /* buffer for audio */ +#define AOUTLEN (64*1024) + void *bmpbuf; +#define BMPLEN (8*32768+1024) + + /* bitmap buffers and states */ + + int bmpp; + int bmplen; + volatile int bmp_state; +#define BMP_NONE 0 +#define BMP_LOADING 1 +#define BMP_LOADED 2 + wait_queue_head_t bmpq; + + + /* DEBI and polled command interface */ + + spinlock_t debilock; + struct mutex dcomlock; + volatile int debitype; + volatile int debilen; + + + /* Recording and playback flags */ + + int rec_mode; + int playing; +#define RP_NONE 0 +#define RP_VIDEO 1 +#define RP_AUDIO 2 +#define RP_AV 3 + + + /* OSD */ + + int osdwin; /* currently active window */ + u16 osdbpp[8]; + struct mutex osd_mutex; + + /* CA */ + + struct ca_slot_info ci_slot[2]; + + enum av7110_video_mode vidmode; + struct dmxdev dmxdev; + struct dvb_demux demux; + + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + + /* for budget mode demux1 */ + struct dmxdev dmxdev1; + struct dvb_demux demux1; + struct dvb_net dvb_net1; + spinlock_t feedlock1; + int feeding1; + u32 ttbp; + unsigned char *grabbing; + struct saa7146_pgtable pt; + struct tasklet_struct vpe_tasklet; + bool full_ts; + + int fe_synced; + struct mutex pid_mutex; + + int video_blank; + struct video_status videostate; + u16 display_panscan; + int display_ar; + int trickmode; +#define TRICK_NONE 0 +#define TRICK_FAST 1 +#define TRICK_SLOW 2 +#define TRICK_FREEZE 3 + struct audio_status audiostate; + + struct dvb_demux_filter *handle2filter[32]; + struct av7110_p2t p2t_filter[MAXFILT]; + struct dvb_filter_pes2ts p2t[2]; + struct ipack ipack[2]; + u8 *kbuf[2]; + + int sinfo; + int feeding; + + int arm_errors; + int registered; + + + /* AV711X */ + + u32 arm_fw; + u32 arm_rtsl; + u32 arm_vid; + u32 arm_app; + u32 avtype; + int arm_ready; + struct task_struct *arm_thread; + wait_queue_head_t arm_wait; + u16 arm_loops; + + void *debi_virt; + dma_addr_t debi_bus; + + u16 pids[DMX_PES_OTHER]; + + struct dvb_ringbuffer ci_rbuffer; + struct dvb_ringbuffer ci_wbuffer; + + struct audio_mixer mixer; + + struct dvb_adapter dvb_adapter; + struct dvb_device *video_dev; + struct dvb_device *audio_dev; + struct dvb_device *ca_dev; + struct dvb_device *osd_dev; + + struct dvb_video_events video_events; + video_size_t video_size; + + u16 wssMode; + u16 wssData; + + struct infrared ir; + + /* firmware stuff */ + unsigned char *bin_fw; + unsigned long size_fw; + + unsigned char *bin_dpram; + unsigned long size_dpram; + + unsigned char *bin_root; + unsigned long size_root; + + struct dvb_frontend* fe; + enum fe_status fe_status; + + struct mutex ioctl_mutex; + + /* crash recovery */ + void (*recover)(struct av7110* av7110); + enum fe_sec_voltage saved_voltage; + enum fe_sec_tone_mode saved_tone; + struct dvb_diseqc_master_cmd saved_master_cmd; + enum fe_sec_mini_cmd saved_minicmd; + + int (*fe_init)(struct dvb_frontend* fe); + int (*fe_read_status)(struct dvb_frontend *fe, enum fe_status *status); + int (*fe_diseqc_reset_overload)(struct dvb_frontend *fe); + int (*fe_diseqc_send_master_cmd)(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd); + int (*fe_diseqc_send_burst)(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd); + int (*fe_set_tone)(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone); + int (*fe_set_voltage)(struct dvb_frontend *fe, + enum fe_sec_voltage voltage); + int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend *fe, + unsigned long cmd); + int (*fe_set_frontend)(struct dvb_frontend *fe); +}; + + +extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid); + +void av7110_ir_handler(struct av7110 *av7110, u32 ircom); +int av7110_set_ir_config(struct av7110 *av7110); +int av7110_ir_init(struct av7110 *av7110); +void av7110_ir_exit(struct av7110 *av7110); + +/* msp3400 i2c subaddresses */ +#define MSP_WR_DEM 0x10 +#define MSP_RD_DEM 0x11 +#define MSP_WR_DSP 0x12 +#define MSP_RD_DSP 0x13 + +extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); +extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); +extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); + + +extern int av7110_init_analog_module(struct av7110 *av7110); +extern int av7110_init_v4l(struct av7110 *av7110); +extern int av7110_exit_v4l(struct av7110 *av7110); + +#endif /* _AV7110_H_ */ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c new file mode 100644 index 000000000000..0bf513c26b6b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c @@ -0,0 +1,1681 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * av7110_av.c: audio and video MPEG decoder stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * the project's page is at https://linuxtv.org + */ + +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" +#include "av7110_ipack.h" + +/* MPEG-2 (ISO 13818 / H.222.0) stream types */ +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define PTS_DTS_FLAGS 0xC0 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +static void p_to_t(u8 const *buf, long int length, u16 pid, + u8 *counter, struct dvb_demux_feed *feed); +static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); + + +int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) +{ + struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; + + if (!(dvbdmxfeed->ts_type & TS_PACKET)) + return 0; + if (buf[3] == 0xe0) // video PES do not have a length in TS + buf[4] = buf[5] = 0; + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + return dvbdmxfeed->cb.ts(buf, len, NULL, 0, + &dvbdmxfeed->feed.ts, NULL); + else + return dvb_filter_pes2ts(p2t, buf, len, 1); +} + +static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) +{ + struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; + + dvbdmxfeed->cb.ts(data, 188, NULL, 0, + &dvbdmxfeed->feed.ts, NULL); + return 0; +} + +int av7110_av_start_record(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed) +{ + int ret = 0; + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + + dprintk(2, "av7110:%p, dvb_demux_feed:%p\n", av7110, dvbdmxfeed); + + if (av7110->playing || (av7110->rec_mode & av)) + return -EBUSY; + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + dvbdmx->recording = 1; + av7110->rec_mode |= av; + + switch (av7110->rec_mode) { + case RP_AUDIO: + dvb_filter_pes2ts_init(&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[0]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + + case RP_VIDEO: + dvb_filter_pes2ts_init(&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[1]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + + case RP_AV: + dvb_filter_pes2ts_init(&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[0]); + dvb_filter_pes2ts_init(&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *) dvbdmx->pesfilter[1]); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + break; + } + return ret; +} + +int av7110_av_start_play(struct av7110 *av7110, int av) +{ + int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->rec_mode) + return -EBUSY; + if (av7110->playing & av) + return -EBUSY; + + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + + if (av7110->playing == RP_NONE) { + av7110_ipack_reset(&av7110->ipack[0]); + av7110_ipack_reset(&av7110->ipack[1]); + } + + av7110->playing |= av; + switch (av7110->playing) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + av7110->sinfo = 0; + break; + case RP_AV: + av7110->sinfo = 0; + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + break; + } + return ret; +} + +int av7110_av_stop(struct av7110 *av7110, int av) +{ + int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); + + if (!(av7110->playing & av) && !(av7110->rec_mode & av)) + return 0; + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + if (av7110->playing) { + av7110->playing &= ~av; + switch (av7110->playing) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + break; + case RP_NONE: + ret = av7110_set_vidmode(av7110, av7110->vidmode); + break; + } + } else { + av7110->rec_mode &= ~av; + switch (av7110->rec_mode) { + case RP_AUDIO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + case RP_VIDEO: + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + case RP_NONE: + break; + } + } + return ret; +} + + +int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) +{ + int len; + u32 sync; + u16 blen; + + if (!dlen) { + wake_up(&buf->queue); + return -1; + } + while (1) { + len = dvb_ringbuffer_avail(buf); + if (len < 6) { + wake_up(&buf->queue); + return -1; + } + sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; + sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; + sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; + sync |= DVB_RINGBUFFER_PEEK(buf, 3); + + if (((sync &~ 0x0f) == 0x000001e0) || + ((sync &~ 0x1f) == 0x000001c0) || + (sync == 0x000001bd)) + break; + printk("resync\n"); + DVB_RINGBUFFER_SKIP(buf, 1); + } + blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; + blen |= DVB_RINGBUFFER_PEEK(buf, 5); + blen += 6; + if (len < blen || blen > dlen) { + //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); + wake_up(&buf->queue); + return -1; + } + + dvb_ringbuffer_read(buf, dest, (size_t) blen); + + dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", + (unsigned long) buf->pread, (unsigned long) buf->pwrite); + wake_up(&buf->queue); + return blen; +} + + +int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, + unsigned int volright) +{ + unsigned int vol, val, balance = 0; + int err; + + dprintk(2, "av7110:%p, \n", av7110); + + av7110->mixer.volume_left = volleft; + av7110->mixer.volume_right = volright; + + switch (av7110->adac_type) { + case DVB_ADAC_TI: + volleft = (volleft * 256) / 1036; + volright = (volright * 256) / 1036; + if (volleft > 0x3f) + volleft = 0x3f; + if (volright > 0x3f) + volright = 0x3f; + if ((err = SendDAC(av7110, 3, 0x80 + volleft))) + return err; + return SendDAC(av7110, 4, volright); + + case DVB_ADAC_CRYSTAL: + volleft = 127 - volleft / 2; + volright = 127 - volright / 2; + i2c_writereg(av7110, 0x20, 0x03, volleft); + i2c_writereg(av7110, 0x20, 0x04, volright); + return 0; + + case DVB_ADAC_MSP34x0: + vol = (volleft > volright) ? volleft : volright; + val = (vol * 0x73 / 255) << 8; + if (vol > 0) + balance = ((volright - volleft) * 127) / vol; + msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ + msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ + return 0; + + case DVB_ADAC_MSP34x5: + vol = (volleft > volright) ? volleft : volright; + val = (vol * 0x73 / 255) << 8; + if (vol > 0) + balance = ((volright - volleft) * 127) / vol; + msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ + return 0; + } + + return 0; +} + +int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) +{ + int ret; + dprintk(2, "av7110:%p, \n", av7110); + + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + + if (!ret && !av7110->playing) { + ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], + 0, av7110->pids[DMX_PES_PCR]); + if (!ret) + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + } + return ret; +} + + +static enum av7110_video_mode sw2mode[16] = { + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, + AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +}; + +static int get_video_format(struct av7110 *av7110, u8 *buf, int count) +{ + int i; + int hsize, vsize; + int sw; + u8 *p; + int ret = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->sinfo) + return 0; + for (i = 7; i < count - 10; i++) { + p = buf + i; + if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) + continue; + p += 4; + hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); + vsize = ((p[1] &0x0F) << 8) | (p[2]); + sw = (p[3] & 0x0F); + ret = av7110_set_vidmode(av7110, sw2mode[sw]); + if (!ret) { + dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); + av7110->sinfo = 1; + } + break; + } + return ret; +} + + +/**************************************************************************** + * I/O buffer management and control + ****************************************************************************/ + +static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, + const u8 *buf, unsigned long count) +{ + unsigned long todo = count; + int free; + + while (todo > 0) { + if (dvb_ringbuffer_free(rbuf) < 2048) { + if (wait_event_interruptible(rbuf->queue, + (dvb_ringbuffer_free(rbuf) >= 2048))) + return count - todo; + } + free = dvb_ringbuffer_free(rbuf); + if (free > todo) + free = todo; + dvb_ringbuffer_write(rbuf, buf, free); + todo -= free; + buf += free; + } + + return count - todo; +} + +static void play_video_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); + + if ((buf[3] & 0xe0) == 0xe0) { + get_video_format(av7110, buf, count); + aux_ring_buffer_write(&av7110->avout, buf, count); + } else + aux_ring_buffer_write(&av7110->aout, buf, count); +} + +static void play_audio_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); + + aux_ring_buffer_write(&av7110->aout, buf, count); +} + + +#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) + +static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + struct dvb_ringbuffer *rb; + u8 *kb; + unsigned long todo = count; + + dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); + + rb = (type) ? &av7110->avout : &av7110->aout; + kb = av7110->kbuf[type]; + + if (!kb) + return -ENOBUFS; + + if (nonblock && !FREE_COND_TS) + return -EWOULDBLOCK; + + while (todo >= TS_SIZE) { + if (!FREE_COND_TS) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(rb->queue, FREE_COND_TS)) + return count - todo; + } + if (copy_from_user(kb, buf, TS_SIZE)) + return -EFAULT; + write_ts_to_decoder(av7110, type, kb, TS_SIZE); + todo -= TS_SIZE; + buf += TS_SIZE; + } + + return count - todo; +} + + +#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ + dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) + +static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + + if (nonblock && !FREE_COND) + return -EWOULDBLOCK; + + while (todo > 0) { + if (!FREE_COND) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->avout.queue, + FREE_COND)) + return count - todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + + if (nonblock && !FREE_COND) + return -EWOULDBLOCK; + + while (todo > 0) { + if (!FREE_COND) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->avout.queue, + FREE_COND)) + return count - todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) + return -EWOULDBLOCK; + + while (todo > 0) { + if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { + if (nonblock) + return count - todo; + if (wait_event_interruptible(av7110->aout.queue, + (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) + return count-todo; + } + n = todo; + if (n > IPACKS * 2) + n = IPACKS * 2; + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); + todo -= n; + buf += n; + } + return count - todo; +} + +void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) +{ + memset(p->pes, 0, TS_SIZE); + p->counter = 0; + p->pos = 0; + p->frags = 0; + if (feed) + p->feed = feed; +} + +static void clear_p2t(struct av7110_p2t *p) +{ + memset(p->pes, 0, TS_SIZE); +// p->counter = 0; + p->pos = 0; + p->frags = 0; +} + + +static int find_pes_header(u8 const *buf, long int length, int *frags) +{ + int c = 0; + int found = 0; + + *frags = 0; + + while (c < length - 3 && !found) { + if (buf[c] == 0x00 && buf[c + 1] == 0x00 && + buf[c + 2] == 0x01) { + switch ( buf[c + 3] ) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM: + case EMM_STREAM: + case PADDING_STREAM: + case DSM_CC_STREAM: + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + found = 1; + break; + + default: + c++; + break; + } + } else + c++; + } + if (c == length - 3 && !found) { + if (buf[length - 1] == 0x00) + *frags = 1; + if (buf[length - 2] == 0x00 && + buf[length - 1] == 0x00) + *frags = 2; + if (buf[length - 3] == 0x00 && + buf[length - 2] == 0x00 && + buf[length - 1] == 0x01) + *frags = 3; + return -1; + } + + return c; +} + +void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) +{ + int c, c2, l, add; + int check, rest; + + c = 0; + c2 = 0; + if (p->frags){ + check = 0; + switch(p->frags) { + case 1: + if (buf[c] == 0x00 && buf[c + 1] == 0x01) { + check = 1; + c += 2; + } + break; + case 2: + if (buf[c] == 0x01) { + check = 1; + c++; + } + break; + case 3: + check = 1; + } + if (check) { + switch (buf[c]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM: + case EMM_STREAM: + case PADDING_STREAM: + case DSM_CC_STREAM: + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + p->pes[0] = 0x00; + p->pes[1] = 0x00; + p->pes[2] = 0x01; + p->pes[3] = buf[c]; + p->pos = 4; + memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); + c += (TS_SIZE - 4) - p->pos; + p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); + clear_p2t(p); + break; + + default: + c = 0; + break; + } + } + p->frags = 0; + } + + if (p->pos) { + c2 = find_pes_header(buf + c, length - c, &p->frags); + if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) + l = c2+c; + else + l = (TS_SIZE - 4) - p->pos; + memcpy(p->pes + p->pos, buf, l); + c += l; + p->pos += l; + p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); + clear_p2t(p); + } + + add = 0; + while (c < length) { + c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); + if (c2 >= 0) { + c2 += c + add; + if (c2 > c){ + p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); + c = c2; + clear_p2t(p); + add = 0; + } else + add = 1; + } else { + l = length - c; + rest = l % (TS_SIZE - 4); + l -= rest; + p_to_t(buf + c, l, pid, &p->counter, p->feed); + memcpy(p->pes, buf + c + l, rest); + p->pos = rest; + c = length; + } + } +} + + +static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) +{ + int i; + int c = 0; + int fill; + u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; + + fill = (TS_SIZE - 4) - length; + if (pes_start) + tshead[1] = 0x40; + if (fill) + tshead[3] = 0x30; + tshead[1] |= (u8)((pid & 0x1F00) >> 8); + tshead[2] |= (u8)(pid & 0x00FF); + tshead[3] |= ((*counter)++ & 0x0F); + memcpy(buf, tshead, 4); + c += 4; + + if (fill) { + buf[4] = fill - 1; + c++; + if (fill > 1) { + buf[5] = 0x00; + c++; + } + for (i = 6; i < fill + 4; i++) { + buf[i] = 0xFF; + c++; + } + } + + return c; +} + + +static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, + struct dvb_demux_feed *feed) +{ + int l, pes_start; + u8 obuf[TS_SIZE]; + long c = 0; + + pes_start = 0; + if (length > 3 && + buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) + switch (buf[3]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM: + case EMM_STREAM: + case PADDING_STREAM: + case DSM_CC_STREAM: + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + pes_start = 1; + break; + + default: + break; + } + + while (c < length) { + memset(obuf, 0, TS_SIZE); + if (length - c >= (TS_SIZE - 4)){ + l = write_ts_header2(pid, counter, pes_start, + obuf, (TS_SIZE - 4)); + memcpy(obuf + l, buf + c, TS_SIZE - l); + c += TS_SIZE - l; + } else { + l = write_ts_header2(pid, counter, pes_start, + obuf, length - c); + memcpy(obuf + l, buf + c, TS_SIZE - l); + c = length; + } + feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, NULL); + pes_start = 0; + } +} + + +static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) +{ + struct ipack *ipack = &av7110->ipack[type]; + + if (buf[1] & TRANS_ERROR) { + av7110_ipack_reset(ipack); + return -1; + } + + if (!(buf[3] & PAYLOAD)) + return -1; + + if (buf[1] & PAY_START) + av7110_ipack_flush(ipack); + + if (buf[3] & ADAPT_FIELD) { + len -= buf[4] + 1; + buf += buf[4] + 1; + if (!len) + return 0; + } + + av7110_ipack_instant_repack(buf + 4, len - 4, ipack); + return 0; +} + + +int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = (struct av7110 *) demux->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) + return 0; + + switch (feed->pes_type) { + case 0: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + return -EINVAL; + break; + case 1: + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) + return -EINVAL; + break; + default: + return -1; + } + + return write_ts_to_decoder(av7110, feed->pes_type, buf, len); +} + + + +/****************************************************************************** + * Video MPEG decoder events + ******************************************************************************/ +void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) +{ + struct dvb_video_events *events = &av7110->video_events; + int wp; + + spin_lock_bh(&events->lock); + + wp = (events->eventw + 1) % MAX_VIDEO_EVENT; + if (wp == events->eventr) { + events->overflow = 1; + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + } + + //FIXME: timestamp? + memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); + events->eventw = wp; + + spin_unlock_bh(&events->lock); + + wake_up_interruptible(&events->wait_queue); +} + + +static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) +{ + struct dvb_video_events *events = &av7110->video_events; + + if (events->overflow) { + events->overflow = 0; + return -EOVERFLOW; + } + if (events->eventw == events->eventr) { + int ret; + + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(events->wait_queue, + events->eventw != events->eventr); + if (ret < 0) + return ret; + } + + spin_lock_bh(&events->lock); + + memcpy(event, &events->events[events->eventr], + sizeof(struct video_event)); + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + + spin_unlock_bh(&events->lock); + + return 0; +} + +/****************************************************************************** + * DVB device file operations + ******************************************************************************/ + +static __poll_t dvb_video_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + __poll_t mask = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + poll_wait(file, &av7110->avout.queue, wait); + + poll_wait(file, &av7110->video_events.wait_queue, wait); + + if (av7110->video_events.eventw != av7110->video_events.eventr) + mask = EPOLLPRI; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (av7110->playing) { + if (FREE_COND) + mask |= (EPOLLOUT | EPOLLWRNORM); + } else { + /* if not playing: may play if asked for */ + mask |= (EPOLLOUT | EPOLLWRNORM); + } + } + + return mask; +} + +static ssize_t dvb_video_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned char c; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + + if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) + return -EPERM; + + if (get_user(c, buf)) + return -EFAULT; + if (c == 0x47 && count % TS_SIZE == 0) + return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); + else + return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); +} + +static __poll_t dvb_audio_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + __poll_t mask = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + poll_wait(file, &av7110->aout.queue, wait); + + if (av7110->playing) { + if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) + mask |= (EPOLLOUT | EPOLLWRNORM); + } else /* if not playing: may play if asked for */ + mask = (EPOLLOUT | EPOLLWRNORM); + + return mask; +} + +static ssize_t dvb_audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned char c; + + dprintk(2, "av7110:%p, \n", av7110); + + if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { + printk(KERN_ERR "not audio source memory\n"); + return -EPERM; + } + + if (get_user(c, buf)) + return -EFAULT; + if (c == 0x47 && count % TS_SIZE == 0) + return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); + else + return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); +} + +static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; + +#define MIN_IFRAME 400000 + +static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) +{ + unsigned i, n; + int progressive = 0; + int match = 0; + + dprintk(2, "av7110:%p, \n", av7110); + + if (len == 0) + return 0; + + if (!(av7110->playing & RP_VIDEO)) { + if (av7110_av_start_play(av7110, RP_VIDEO) < 0) + return -EBUSY; + } + + /* search in buf for instances of 00 00 01 b5 1? */ + for (i = 0; i < len; i++) { + unsigned char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (match == 5) { + progressive = c & 0x08; + match = 0; + } + if (c == 0x00) { + match = (match == 1 || match == 2) ? 2 : 1; + continue; + } + switch (match++) { + case 2: if (c == 0x01) + continue; + break; + case 3: if (c == 0xb5) + continue; + break; + case 4: if ((c & 0xf0) == 0x10) + continue; + break; + } + match = 0; + } + + /* setting n always > 1, fixes problems when playing stillframes + consisting of I- and P-Frames */ + n = MIN_IFRAME / len + 1; + + /* FIXME: nonblock? */ + dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); + + for (i = 0; i < n; i++) + dvb_play(av7110, buf, len, 0, 1); + + av7110_ipack_flush(&av7110->ipack[1]); + + if (progressive) + return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); + else + return 0; +} + +#ifdef CONFIG_COMPAT +struct compat_video_still_picture { + compat_uptr_t iFrame; + int32_t size; +}; +#define VIDEO_STILLPICTURE32 _IOW('o', 30, struct compat_video_still_picture) + +struct compat_video_event { + __s32 type; + /* unused, make sure to use atomic time for y2038 if it ever gets used */ + compat_long_t timestamp; + union { + video_size_t size; + unsigned int frame_rate; /* in frames per 1000sec */ + unsigned char vsync_field; /* unknown/odd/even/progressive */ + } u; +}; +#define VIDEO_GET_EVENT32 _IOR('o', 28, struct compat_video_event) + +static int dvb_compat_video_get_event(struct av7110 *av7110, + struct compat_video_event *event, int flags) +{ + struct video_event ev; + int ret; + + ret = dvb_video_get_event(av7110, &ev, flags); + + *event = (struct compat_video_event) { + .type = ev.type, + .timestamp = ev.timestamp, + .u.size = ev.u.size, + }; + + return ret; +} +#endif + +static int dvb_video_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + int ret = 0; + + dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && + cmd != VIDEO_GET_SIZE ) { + return -EPERM; + } + } + + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + + switch (cmd) { + case VIDEO_STOP: + av7110->videostate.play_state = VIDEO_STOPPED; + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) + ret = av7110_av_stop(av7110, RP_VIDEO); + else + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, + av7110->videostate.video_blank ? 0 : 1); + if (!ret) + av7110->trickmode = TRICK_NONE; + break; + + case VIDEO_PLAY: + av7110->trickmode = TRICK_NONE; + if (av7110->videostate.play_state == VIDEO_FREEZED) { + av7110->videostate.play_state = VIDEO_PLAYING; + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (ret) + break; + } + if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { + if (av7110->playing == RP_AV) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + if (ret) + break; + av7110->playing &= ~RP_VIDEO; + } + ret = av7110_av_start_play(av7110, RP_VIDEO); + } + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) + av7110->videostate.play_state = VIDEO_PLAYING; + break; + + case VIDEO_FREEZE: + av7110->videostate.play_state = VIDEO_FREEZED; + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); + else + ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); + if (!ret) + av7110->trickmode = TRICK_FREEZE; + break; + + case VIDEO_CONTINUE: + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) { + av7110->videostate.play_state = VIDEO_PLAYING; + av7110->trickmode = TRICK_NONE; + } + break; + + case VIDEO_SELECT_SOURCE: + av7110->videostate.stream_source = (video_stream_source_t) arg; + break; + + case VIDEO_SET_BLANK: + av7110->videostate.video_blank = (int) arg; + break; + + case VIDEO_GET_STATUS: + memcpy(parg, &av7110->videostate, sizeof(struct video_status)); + break; + +#ifdef CONFIG_COMPAT + case VIDEO_GET_EVENT32: + ret = dvb_compat_video_get_event(av7110, parg, file->f_flags); + break; +#endif + + case VIDEO_GET_EVENT: + ret = dvb_video_get_event(av7110, parg, file->f_flags); + break; + + case VIDEO_GET_SIZE: + memcpy(parg, &av7110->video_size, sizeof(video_size_t)); + break; + + case VIDEO_SET_DISPLAY_FORMAT: + { + video_displayformat_t format = (video_displayformat_t) arg; + switch (format) { + case VIDEO_PAN_SCAN: + av7110->display_panscan = VID_PAN_SCAN_PREF; + break; + case VIDEO_LETTER_BOX: + av7110->display_panscan = VID_VC_AND_PS_PREF; + break; + case VIDEO_CENTER_CUT_OUT: + av7110->display_panscan = VID_CENTRE_CUT_PREF; + break; + default: + ret = -EINVAL; + } + if (ret < 0) + break; + av7110->videostate.display_format = format; + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, av7110->display_panscan); + break; + } + + case VIDEO_SET_FORMAT: + if (arg > 1) { + ret = -EINVAL; + break; + } + av7110->display_ar = arg; + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) arg); + break; + +#ifdef CONFIG_COMPAT + case VIDEO_STILLPICTURE32: + { + struct compat_video_still_picture *pic = + (struct compat_video_still_picture *) parg; + av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + ret = play_iframe(av7110, compat_ptr(pic->iFrame), + pic->size, file->f_flags & O_NONBLOCK); + break; + } +#endif + + case VIDEO_STILLPICTURE: + { + struct video_still_picture *pic = + (struct video_still_picture *) parg; + av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + ret = play_iframe(av7110, pic->iFrame, pic->size, + file->f_flags & O_NONBLOCK); + break; + } + + case VIDEO_FAST_FORWARD: + //note: arg is ignored by firmware + if (av7110->playing & RP_VIDEO) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + else + ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); + if (!ret) { + av7110->trickmode = TRICK_FAST; + av7110->videostate.play_state = VIDEO_PLAYING; + } + break; + + case VIDEO_SLOWMOTION: + if (av7110->playing&RP_VIDEO) { + if (av7110->trickmode != TRICK_SLOW) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } else { + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } + if (!ret) { + av7110->trickmode = TRICK_SLOW; + av7110->videostate.play_state = VIDEO_PLAYING; + } + break; + + case VIDEO_GET_CAPABILITIES: + *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | + VIDEO_CAP_SYS | VIDEO_CAP_PROG; + break; + + case VIDEO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110_ipack_reset(&av7110->ipack[1]); + if (av7110->playing == RP_AV) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + if (ret) + break; + if (av7110->trickmode == TRICK_FAST) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + if (av7110->trickmode == TRICK_SLOW) { + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Slow, 2, 0, 0); + if (!ret) + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); + } + if (av7110->trickmode == TRICK_FREEZE) + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); + } + break; + + case VIDEO_SET_STREAMTYPE: + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + mutex_unlock(&av7110->ioctl_mutex); + return ret; +} + +static int dvb_audio_ioctl(struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + int ret = 0; + + dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + + if (((file->f_flags & O_ACCMODE) == O_RDONLY) && + (cmd != AUDIO_GET_STATUS)) + return -EPERM; + + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + + switch (cmd) { + case AUDIO_STOP: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + ret = av7110_av_stop(av7110, RP_AUDIO); + else + ret = audcom(av7110, AUDIO_CMD_MUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_STOPPED; + break; + + case AUDIO_PLAY: + if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) + ret = av7110_av_start_play(av7110, RP_AUDIO); + if (!ret) + ret = audcom(av7110, AUDIO_CMD_UNMUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_PLAYING; + break; + + case AUDIO_PAUSE: + ret = audcom(av7110, AUDIO_CMD_MUTE); + if (!ret) + av7110->audiostate.play_state = AUDIO_PAUSED; + break; + + case AUDIO_CONTINUE: + if (av7110->audiostate.play_state == AUDIO_PAUSED) { + av7110->audiostate.play_state = AUDIO_PLAYING; + ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); + } + break; + + case AUDIO_SELECT_SOURCE: + av7110->audiostate.stream_source = (audio_stream_source_t) arg; + break; + + case AUDIO_SET_MUTE: + { + ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); + if (!ret) + av7110->audiostate.mute_state = (int) arg; + break; + } + + case AUDIO_SET_AV_SYNC: + av7110->audiostate.AV_sync_state = (int) arg; + ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); + break; + + case AUDIO_SET_BYPASS_MODE: + if (FW_VERSION(av7110->arm_app) < 0x2621) + ret = -EINVAL; + av7110->audiostate.bypass_mode = (int)arg; + break; + + case AUDIO_CHANNEL_SELECT: + av7110->audiostate.channel_select = (audio_channel_select_t) arg; + switch(av7110->audiostate.channel_select) { + case AUDIO_STEREO: + ret = audcom(av7110, AUDIO_CMD_STEREO); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x49); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); + } + break; + case AUDIO_MONO_LEFT: + ret = audcom(av7110, AUDIO_CMD_MONO_L); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x4a); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); + } + break; + case AUDIO_MONO_RIGHT: + ret = audcom(av7110, AUDIO_CMD_MONO_R); + if (!ret) { + if (av7110->adac_type == DVB_ADAC_CRYSTAL) + i2c_writereg(av7110, 0x20, 0x02, 0x45); + else if (av7110->adac_type == DVB_ADAC_MSP34x5) + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); + } + break; + default: + ret = -EINVAL; + break; + } + break; + + case AUDIO_GET_STATUS: + memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); + break; + + case AUDIO_GET_CAPABILITIES: + if (FW_VERSION(av7110->arm_app) < 0x2621) + *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; + else + *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | + AUDIO_CAP_MP1 | AUDIO_CAP_MP2; + break; + + case AUDIO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110_ipack_reset(&av7110->ipack[0]); + if (av7110->playing == RP_AV) + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + break; + + case AUDIO_SET_ID: + break; + + case AUDIO_SET_MIXER: + { + struct audio_mixer *amix = (struct audio_mixer *)parg; + ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); + break; + } + + case AUDIO_SET_STREAMTYPE: + break; + + default: + ret = -ENOIOCTLCMD; + } + + mutex_unlock(&av7110->ioctl_mutex); + return ret; +} + + +static int dvb_video_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((err = dvb_generic_open(inode, file)) < 0) + return err; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110->video_blank = 1; + av7110->audiostate.AV_sync_state = 1; + av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; + + /* empty event queue */ + av7110->video_events.eventr = av7110->video_events.eventw = 0; + } + + return 0; +} + +static int dvb_video_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + av7110_av_stop(av7110, RP_VIDEO); + } + + return dvb_generic_release(inode, file); +} + +static int dvb_audio_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err = dvb_generic_open(inode, file); + + dprintk(2, "av7110:%p, \n", av7110); + + if (err < 0) + return err; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; + return 0; +} + +static int dvb_audio_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(2, "av7110:%p, \n", av7110); + + av7110_av_stop(av7110, RP_AUDIO); + return dvb_generic_release(inode, file); +} + + + +/****************************************************************************** + * driver registration + ******************************************************************************/ + +static const struct file_operations dvb_video_fops = { + .owner = THIS_MODULE, + .write = dvb_video_write, + .unlocked_ioctl = dvb_generic_ioctl, + .compat_ioctl = dvb_generic_ioctl, + .open = dvb_video_open, + .release = dvb_video_release, + .poll = dvb_video_poll, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_video = { + .priv = NULL, + .users = 6, + .readers = 5, /* arbitrary */ + .writers = 1, + .fops = &dvb_video_fops, + .kernel_ioctl = dvb_video_ioctl, +}; + +static const struct file_operations dvb_audio_fops = { + .owner = THIS_MODULE, + .write = dvb_audio_write, + .unlocked_ioctl = dvb_generic_ioctl, + .compat_ioctl = dvb_generic_ioctl, + .open = dvb_audio_open, + .release = dvb_audio_release, + .poll = dvb_audio_poll, + .llseek = noop_llseek, +}; + +static struct dvb_device dvbdev_audio = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_audio_fops, + .kernel_ioctl = dvb_audio_ioctl, +}; + + +int av7110_av_register(struct av7110 *av7110) +{ + av7110->audiostate.AV_sync_state = 0; + av7110->audiostate.mute_state = 0; + av7110->audiostate.play_state = AUDIO_STOPPED; + av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; + av7110->audiostate.channel_select = AUDIO_STEREO; + av7110->audiostate.bypass_mode = 0; + + av7110->videostate.video_blank = 0; + av7110->videostate.play_state = VIDEO_STOPPED; + av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; + av7110->videostate.video_format = VIDEO_FORMAT_4_3; + av7110->videostate.display_format = VIDEO_LETTER_BOX; + av7110->display_ar = VIDEO_FORMAT_4_3; + av7110->display_panscan = VID_VC_AND_PS_PREF; + + init_waitqueue_head(&av7110->video_events.wait_queue); + spin_lock_init(&av7110->video_events.lock); + av7110->video_events.eventw = av7110->video_events.eventr = 0; + av7110->video_events.overflow = 0; + memset(&av7110->video_size, 0, sizeof (video_size_t)); + + dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, + &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0); + + dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, + &dvbdev_audio, av7110, DVB_DEVICE_AUDIO, 0); + + return 0; +} + +void av7110_av_unregister(struct av7110 *av7110) +{ + dvb_unregister_device(av7110->audio_dev); + dvb_unregister_device(av7110->video_dev); +} + +int av7110_av_init(struct av7110 *av7110) +{ + void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; + int i, ret; + + for (i = 0; i < 2; i++) { + struct ipack *ipack = av7110->ipack + i; + + ret = av7110_ipack_init(ipack, IPACKS, play[i]); + if (ret < 0) { + if (i) + av7110_ipack_free(--ipack); + goto out; + } + ipack->data = av7110; + } + + dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); + dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); + + av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); + av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; +out: + return ret; +} + +void av7110_av_exit(struct av7110 *av7110) +{ + av7110_ipack_free(&av7110->ipack[0]); + av7110_ipack_free(&av7110->ipack[1]); +} diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h new file mode 100644 index 000000000000..71bbd4391f57 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AV7110_AV_H_ +#define _AV7110_AV_H_ + +struct av7110; + +extern int av7110_set_vidmode(struct av7110 *av7110, + enum av7110_video_mode mode); + +extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); +extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); +extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); + +extern int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, + unsigned int volright); +extern int av7110_av_stop(struct av7110 *av7110, int av); +extern int av7110_av_start_record(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed); +extern int av7110_av_start_play(struct av7110 *av7110, int av); + +extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); + +extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); +extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); + +extern int av7110_av_register(struct av7110 *av7110); +extern void av7110_av_unregister(struct av7110 *av7110); +extern int av7110_av_init(struct av7110 *av7110); +extern void av7110_av_exit(struct av7110 *av7110); + + +#endif /* _AV7110_AV_H_ */ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c new file mode 100644 index 000000000000..c1338e074a3d --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * av7110_ca.c: CA and CI stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * the project's page is at https://linuxtv.org + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_ca.h" + + +void CI_handle(struct av7110 *av7110, u8 *data, u16 len) +{ + dprintk(8, "av7110:%p\n",av7110); + + if (len < 3) + return; + switch (data[0]) { + case CI_MSG_CI_INFO: + if (data[2] != 1 && data[2] != 2) + break; + switch (data[1]) { + case 0: + av7110->ci_slot[data[2] - 1].flags = 0; + break; + case 1: + av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; + break; + case 2: + av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; + break; + } + break; + case CI_SWITCH_PRG_REPLY: + //av7110->ci_stat=data[1]; + break; + default: + break; + } +} + + +void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) +{ + if (dvb_ringbuffer_free(cibuf) < len + 2) + return; + + DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); + DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); + dvb_ringbuffer_write(cibuf, data, len); + wake_up_interruptible(&cibuf->queue); +} + + +/****************************************************************************** + * CI link layer file ops + ******************************************************************************/ + +static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) +{ + struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; + void *data; + + for (p = tab; *p; p++) { + data = vmalloc(size); + if (!data) { + while (p-- != tab) { + vfree(p[0]->data); + p[0]->data = NULL; + } + return -ENOMEM; + } + dvb_ringbuffer_init(*p, data, size); + } + return 0; +} + +static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +{ + dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); + dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); +} + +static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +{ + vfree(cirbuf->data); + cirbuf->data = NULL; + vfree(ciwbuf->data); + ciwbuf->data = NULL; +} + +static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, + int slots, struct ca_slot_info *slot) +{ + int i; + int len = 0; + u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; + + for (i = 0; i < 2; i++) { + if (slots & (1 << i)) + len += 8; + } + + if (dvb_ringbuffer_free(cibuf) < len) + return -EBUSY; + + for (i = 0; i < 2; i++) { + if (slots & (1 << i)) { + msg[2] = i; + dvb_ringbuffer_write(cibuf, msg, 8); + slot[i].flags = 0; + } + } + + return 0; +} + +static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + int free; + int non_blocking = file->f_flags & O_NONBLOCK; + u8 *page = (u8 *)__get_free_page(GFP_USER); + int res; + + if (!page) + return -ENOMEM; + + res = -EINVAL; + if (count > 2048) + goto out; + + res = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + free = dvb_ringbuffer_free(cibuf); + if (count + 2 > free) { + res = -EWOULDBLOCK; + if (non_blocking) + goto out; + res = -ERESTARTSYS; + if (wait_event_interruptible(cibuf->queue, + (dvb_ringbuffer_free(cibuf) >= count + 2))) + goto out; + } + + DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); + DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); + + res = dvb_ringbuffer_write(cibuf, page, count); +out: + free_page((unsigned long)page); + return res; +} + +static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + int avail; + int non_blocking = file->f_flags & O_NONBLOCK; + ssize_t len; + + if (!cibuf->data || !count) + return 0; + if (non_blocking && (dvb_ringbuffer_empty(cibuf))) + return -EWOULDBLOCK; + if (wait_event_interruptible(cibuf->queue, + !dvb_ringbuffer_empty(cibuf))) + return -ERESTARTSYS; + avail = dvb_ringbuffer_avail(cibuf); + if (avail < 4) + return 0; + len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; + len |= DVB_RINGBUFFER_PEEK(cibuf, 1); + if (avail < len + 2 || count < len) + return -EINVAL; + DVB_RINGBUFFER_SKIP(cibuf, 2); + + return dvb_ringbuffer_read_user(cibuf, buf, len); +} + +static int dvb_ca_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err = dvb_generic_open(inode, file); + + dprintk(8, "av7110:%p\n",av7110); + + if (err < 0) + return err; + ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); + return 0; +} + +static __poll_t dvb_ca_poll (struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; + struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; + __poll_t mask = 0; + + dprintk(8, "av7110:%p\n",av7110); + + poll_wait(file, &rbuf->queue, wait); + poll_wait(file, &wbuf->queue, wait); + + if (!dvb_ringbuffer_empty(rbuf)) + mask |= (EPOLLIN | EPOLLRDNORM); + + if (dvb_ringbuffer_free(wbuf) > 1024) + mask |= (EPOLLOUT | EPOLLWRNORM); + + return mask; +} + +static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + int ret = 0; + + dprintk(8, "av7110:%p\n",av7110); + + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + + switch (cmd) { + case CA_RESET: + ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, + &av7110->ci_slot[0]); + break; + case CA_GET_CAP: + { + struct ca_caps cap; + + cap.slot_num = 2; + cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI) | CA_DESCR; + cap.descr_num = 16; + cap.descr_type = CA_ECD; + memcpy(parg, &cap, sizeof(cap)); + break; + } + + case CA_GET_SLOT_INFO: + { + struct ca_slot_info *info=(struct ca_slot_info *)parg; + + if (info->num < 0 || info->num > 1) { + mutex_unlock(&av7110->ioctl_mutex); + return -EINVAL; + } + av7110->ci_slot[info->num].num = info->num; + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(struct ca_slot_info)); + break; + } + + case CA_GET_MSG: + break; + + case CA_SEND_MSG: + break; + + case CA_GET_DESCR_INFO: + { + struct ca_descr_info info; + + info.num = 16; + info.type = CA_ECD; + memcpy(parg, &info, sizeof (info)); + break; + } + + case CA_SET_DESCR: + { + struct ca_descr *descr = (struct ca_descr*) parg; + + if (descr->index >= 16 || descr->parity > 1) { + mutex_unlock(&av7110->ioctl_mutex); + return -EINVAL; + } + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, + (descr->index<<8)|descr->parity, + (descr->cw[0]<<8)|descr->cw[1], + (descr->cw[2]<<8)|descr->cw[3], + (descr->cw[4]<<8)|descr->cw[5], + (descr->cw[6]<<8)|descr->cw[7]); + break; + } + + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&av7110->ioctl_mutex); + return ret; +} + +static ssize_t dvb_ca_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(8, "av7110:%p\n",av7110); + return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); +} + +static ssize_t dvb_ca_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + dprintk(8, "av7110:%p\n",av7110); + return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); +} + +static const struct file_operations dvb_ca_fops = { + .owner = THIS_MODULE, + .read = dvb_ca_read, + .write = dvb_ca_write, + .unlocked_ioctl = dvb_generic_ioctl, + .open = dvb_ca_open, + .release = dvb_generic_release, + .poll = dvb_ca_poll, + .llseek = default_llseek, +}; + +static struct dvb_device dvbdev_ca = { + .priv = NULL, + .users = 1, + .writers = 1, + .fops = &dvb_ca_fops, + .kernel_ioctl = dvb_ca_ioctl, +}; + + +int av7110_ca_register(struct av7110 *av7110) +{ + return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, + &dvbdev_ca, av7110, DVB_DEVICE_CA, 0); +} + +void av7110_ca_unregister(struct av7110 *av7110) +{ + dvb_unregister_device(av7110->ca_dev); +} + +int av7110_ca_init(struct av7110* av7110) +{ + return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); +} + +void av7110_ca_exit(struct av7110* av7110) +{ + ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); +} diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h new file mode 100644 index 000000000000..a6e3f2955730 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AV7110_CA_H_ +#define _AV7110_CA_H_ + +struct av7110; + +extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); +extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); + +extern int av7110_ca_register(struct av7110 *av7110); +extern void av7110_ca_unregister(struct av7110 *av7110); +extern int av7110_ca_init(struct av7110* av7110); +extern void av7110_ca_exit(struct av7110* av7110); + +#endif /* _AV7110_CA_H_ */ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c new file mode 100644 index 000000000000..93ca31e38ddd --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c @@ -0,0 +1,1204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * av7110_hw.c: av7110 low level hardware access and firmware interface + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * the project's page is at https://linuxtv.org + */ + +/* for debugging ARM communication: */ +//#define COM_DEBUG + +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" + +#define _NOHANDSHAKE + +/* + * Max transfer size done by av7110_fw_cmd() + * + * The maximum size passed to this function is 6 bytes. The buffer also + * uses two additional ones for type and size. So, 8 bytes is enough. + */ +#define MAX_XFER_SIZE 8 + +/**************************************************************************** + * DEBI functions + ****************************************************************************/ + +/* This DEBI code is based on the Stradis driver + by Nathan Laredo */ + +int av7110_debiwrite(struct av7110 *av7110, u32 config, + int addr, u32 val, unsigned int count) +{ + struct saa7146_dev *dev = av7110->dev; + + if (count > 32764) { + printk("%s: invalid count %d\n", __func__, count); + return -1; + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done failed\n", __func__); + return -1; + } + saa7146_write(dev, DEBI_CONFIG, config); + if (count <= 4) /* immediate transfer */ + saa7146_write(dev, DEBI_AD, val); + else /* block transfer */ + saa7146_write(dev, DEBI_AD, av7110->debi_bus); + saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); + saa7146_write(dev, MC2, (2 << 16) | 2); + return 0; +} + +u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count) +{ + struct saa7146_dev *dev = av7110->dev; + u32 result = 0; + + if (count > 32764) { + printk("%s: invalid count %d\n", __func__, count); + return 0; + } + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #1 failed\n", __func__); + return 0; + } + saa7146_write(dev, DEBI_AD, av7110->debi_bus); + saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + + saa7146_write(dev, DEBI_CONFIG, config); + saa7146_write(dev, MC2, (2 << 16) | 2); + if (count > 4) + return count; + if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { + printk("%s: wait_for_debi_done #2 failed\n", __func__); + return 0; + } + + result = saa7146_read(dev, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + return result; +} + + + +/* av7110 ARM core boot stuff */ +#if 0 +void av7110_reset_arm(struct av7110 *av7110) +{ + saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); + + /* Disable DEBI and GPIO irq */ + SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + + saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); + msleep(30); /* the firmware needs some time to initialize */ + + ARM_ResetMailBox(av7110); + + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); + + av7110->arm_ready = 1; + dprintk(1, "reset ARM\n"); +} +#endif /* 0 */ + +static int waitdebi(struct av7110 *av7110, int adr, int state) +{ + int k; + + dprintk(4, "%p\n", av7110); + + for (k = 0; k < 100; k++) { + if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) + return 0; + udelay(5); + } + return -ETIMEDOUT; +} + +static int load_dram(struct av7110 *av7110, u32 *data, int len) +{ + int i; + int blocks, rest; + u32 base, bootblock = AV7110_BOOT_BLOCK; + + dprintk(4, "%p\n", av7110); + + blocks = len / AV7110_BOOT_MAX_SIZE; + rest = len % AV7110_BOOT_MAX_SIZE; + base = DRAM_START_CODE; + + for (i = 0; i < blocks; i++) { + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); + return -ETIMEDOUT; + } + dprintk(4, "writing DRAM block %d\n", i); + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); + bootblock ^= 0x1400; + iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + base += AV7110_BOOT_MAX_SIZE; + } + + if (rest > 0) { + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); + return -ETIMEDOUT; + } + if (rest > 4) + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); + else + mwdebi(av7110, DEBISWAB, bootblock, + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); + + iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + } + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); + return -ETIMEDOUT; + } + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { + printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + + +/* we cannot write av7110 DRAM directly, so load a bootloader into + * the DPRAM which implements a simple boot protocol */ +int av7110_bootarm(struct av7110 *av7110) +{ + const struct firmware *fw; + const char *fw_name = "av7110/bootcode.bin"; + struct saa7146_dev *dev = av7110->dev; + u32 ret; + int i; + + dprintk(4, "%p\n", av7110); + + av7110->arm_ready = 0; + + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); + + /* Disable DEBI and GPIO irq */ + SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + + /* enable DEBI */ + saa7146_write(av7110->dev, MC1, 0x08800880); + saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); + saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* test DEBI */ + iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); + /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ + iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); + + if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { + printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", + ret, 0x10325476); + return -1; + } + for (i = 0; i < 8192; i += 4) + iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); + dprintk(2, "debi test OK\n"); + + /* boot */ + dprintk(1, "load boot code\n"); + saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); + //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); + //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); + + ret = request_firmware(&fw, fw_name, &dev->pci->dev); + if (ret) { + printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", + fw_name); + return ret; + } + + mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); + release_firmware(fw); + iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); + + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out\n"); + return -ETIMEDOUT; + } + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); + mdelay(1); + + dprintk(1, "load dram code\n"); + if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): load_dram() failed\n"); + return -1; + } + + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); + mdelay(1); + + dprintk(1, "load dpram code\n"); + mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); + + if (saa7146_wait_for_debi_done(av7110->dev, 1)) { + printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out after loading DRAM\n"); + return -ETIMEDOUT; + } + saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); + msleep(30); /* the firmware needs some time to initialize */ + + //ARM_ClearIrq(av7110); + ARM_ResetMailBox(av7110); + SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); + SAA7146_IER_ENABLE(av7110->dev, MASK_03); + + av7110->arm_errors = 0; + av7110->arm_ready = 1; + return 0; +} +MODULE_FIRMWARE("av7110/bootcode.bin"); + +/**************************************************************************** + * DEBI command polling + ****************************************************************************/ + +int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) +{ + unsigned long start; + u32 stat; + int err; + + if (FW_VERSION(av7110->arm_app) <= 0x261c) { + /* not supported by old firmware */ + msleep(50); + return 0; + } + + /* new firmware */ + start = jiffies; + for (;;) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + mutex_unlock(&av7110->dcomlock); + if ((stat & flags) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", + __func__, stat & flags); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +{ + int i; + unsigned long start; + char *type = NULL; + u16 flags[2] = {0, 0}; + u32 stat; + int err; + +// dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -ENXIO; + } + + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); + av7110->arm_errors++; + return -ETIMEDOUT; + } + msleep(1); + } + + if (FW_VERSION(av7110->arm_app) <= 0x261f) + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); + +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + + switch ((buf[0] >> 8) & 0xff) { + case COMTYPE_PIDFILTER: + case COMTYPE_ENCODER: + case COMTYPE_REC_PLAY: + case COMTYPE_MPEGDECODER: + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQFull; + break; + case COMTYPE_OSD: + type = "OSD"; + flags[0] = OSDQOver; + flags[1] = OSDQFull; + break; + case COMTYPE_MISC: + if (FW_VERSION(av7110->arm_app) >= 0x261d) { + type = "MSG"; + flags[0] = GPMQOver; + flags[1] = GPMQBusy; + } + break; + default: + break; + } + + if (type != NULL) { + /* non-immediate COMMAND type */ + start = jiffies; + for (;;) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & flags[0]) { + printk(KERN_ERR "%s: %s QUEUE overflow\n", + __func__, type); + return -1; + } + if ((stat & flags[1]) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", + __func__, type); + av7110->arm_errors++; + return -ETIMEDOUT; + } + msleep(1); + } + } + + for (i = 2; i < length; i++) + wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); + + if (length) + wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); + else + wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); + + wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); + + if (FW_VERSION(av7110->arm_app) <= 0x261f) + wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); + +#ifdef COM_DEBUG + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", + __func__, (buf[0] >> 8) & 0xff); + return -ETIMEDOUT; + } + msleep(1); + } + + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & GPMQOver) { + printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); + return -ENOSPC; + } + else if (stat & OSDQOver) { + printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); + return -ENOSPC; + } +#endif + + return 0; +} + +static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +{ + int ret; + +// dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -1; + } + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + ret = __av7110_send_fw_cmd(av7110, buf, length); + mutex_unlock(&av7110->dcomlock); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", + __func__, ret); + return ret; +} + +int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) +{ + va_list args; + u16 buf[MAX_XFER_SIZE]; + int i, ret; + +// dprintk(4, "%p\n", av7110); + + if (2 + num > ARRAY_SIZE(buf)) { + printk(KERN_WARNING + "%s: %s len=%d is too big!\n", + KBUILD_MODNAME, __func__, num); + return -EINVAL; + } + + buf[0] = ((type << 8) | com); + buf[1] = num; + + if (num) { + va_start(args, num); + for (i = 0; i < num; i++) + buf[i + 2] = va_arg(args, u32); + va_end(args); + } + + ret = av7110_send_fw_cmd(av7110, buf, num + 2); + if (ret && ret != -ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); + return ret; +} + +#if 0 +int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) +{ + int i, ret; + u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(4, "%p\n", av7110); + + for(i = 0; i < len && i < 32; i++) + { + if(i % 2 == 0) + cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; + else + cmd[(i / 2) + 2] |= buf[i]; + } + + ret = av7110_send_fw_cmd(av7110, cmd, 18); + if (ret && ret != -ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); + return ret; +} +#endif /* 0 */ + +int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, + int request_buf_len, u16 *reply_buf, int reply_buf_len) +{ + int err; + s16 i; + unsigned long start; +#ifdef COM_DEBUG + u32 stat; +#endif + + dprintk(4, "%p\n", av7110); + + if (!av7110->arm_ready) { + dprintk(1, "arm not ready.\n"); + return -1; + } + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { + mutex_unlock(&av7110->dcomlock); + printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); + return err; + } + + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_FREE); + if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } +#ifdef _NOHANDSHAKE + msleep(1); +#endif + } + +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + +#ifdef COM_DEBUG + stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); + if (stat & GPMQOver) { + printk(KERN_ERR "%s: GPMQOver\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -1; + } + else if (stat & OSDQOver) { + printk(KERN_ERR "%s: OSDQOver\n", __func__); + mutex_unlock(&av7110->dcomlock); + return -1; + } +#endif + + for (i = 0; i < reply_buf_len; i++) + reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); + + mutex_unlock(&av7110->dcomlock); + return 0; +} + +static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) +{ + int ret; + ret = av7110_fw_request(av7110, &tag, 0, buf, length); + if (ret) + printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); + return ret; +} + + +/**************************************************************************** + * Firmware commands + ****************************************************************************/ + +/* get version of the firmware ROM, RTSL, video ucode and ARM application */ +int av7110_firmversion(struct av7110 *av7110) +{ + u16 buf[20]; + u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); + + dprintk(4, "%p\n", av7110); + + if (av7110_fw_query(av7110, tag, buf, 16)) { + printk("dvb-ttpci: failed to boot firmware @ card %d\n", + av7110->dvb_adapter.num); + return -EIO; + } + + av7110->arm_fw = (buf[0] << 16) + buf[1]; + av7110->arm_rtsl = (buf[2] << 16) + buf[3]; + av7110->arm_vid = (buf[4] << 16) + buf[5]; + av7110->arm_app = (buf[6] << 16) + buf[7]; + av7110->avtype = (buf[8] << 16) + buf[9]; + + printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", + av7110->dvb_adapter.num, av7110->arm_fw, + av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); + + /* print firmware capabilities */ + if (FW_CI_LL_SUPPORT(av7110->arm_app)) + printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", + av7110->dvb_adapter.num); + else + printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", + av7110->dvb_adapter.num); + + return 0; +} + + +int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) +{ + int i, ret; + u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(4, "%p\n", av7110); + + if (len > 10) + len = 10; + + buf[1] = len + 2; + buf[2] = len; + + if (burst != -1) + buf[3] = burst ? 0x01 : 0x00; + else + buf[3] = 0xffff; + + for (i = 0; i < len; i++) + buf[i + 4] = msg[i]; + + ret = av7110_send_fw_cmd(av7110, buf, 18); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); + return ret; +} + + +#ifdef CONFIG_DVB_AV7110_OSD + +static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); +} + +static inline int SetBlend_(struct av7110 *av7110, u8 windownr, + enum av7110_osd_palette_type colordepth, u16 index, u8 blending) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, + windownr, colordepth, index, blending); +} + +static inline int SetColor_(struct av7110 *av7110, u8 windownr, + enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, + windownr, colordepth, index, colorhi, colorlo); +} + +static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, + u16 colorfg, u16 colorbg) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, + windownr, fontsize, colorfg, colorbg); +} + +static int FlushText(struct av7110 *av7110) +{ + unsigned long start; + int err; + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + start = jiffies; + while (1) { + err = time_after(jiffies, start + ARM_WAIT_OSD); + if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) + break; + if (err) { + printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } + mutex_unlock(&av7110->dcomlock); + return 0; +} + +static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) +{ + int i, ret; + unsigned long start; + int length = strlen(buf) + 1; + u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; + + if (mutex_lock_interruptible(&av7110->dcomlock)) + return -ERESTARTSYS; + + start = jiffies; + while (1) { + ret = time_after(jiffies, start + ARM_WAIT_OSD); + if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) + break; + if (ret) { + printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#ifndef _NOHANDSHAKE + start = jiffies; + while (1) { + ret = time_after(jiffies, start + ARM_WAIT_SHAKE); + if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) + break; + if (ret) { + printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", + __func__); + mutex_unlock(&av7110->dcomlock); + return -ETIMEDOUT; + } + msleep(1); + } +#endif + for (i = 0; i < length / 2; i++) + wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, + swab16(*(u16 *)(buf + 2 * i)), 2); + if (length & 1) + wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); + ret = __av7110_send_fw_cmd(av7110, cbuf, 5); + mutex_unlock(&av7110->dcomlock); + if (ret && ret!=-ERESTARTSYS) + printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); + return ret; +} + +static inline int DrawLine(struct av7110 *av7110, u8 windownr, + u16 x, u16 y, u16 dx, u16 dy, u16 color) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, + windownr, x, y, dx, dy, color); +} + +static inline int DrawBlock(struct av7110 *av7110, u8 windownr, + u16 x, u16 y, u16 dx, u16 dy, u16 color) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, + windownr, x, y, dx, dy, color); +} + +static inline int HideWindow(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); +} + +static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); +} + +static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); +} + +static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); +} + +static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, + osd_raw_window_t disptype, + u16 width, u16 height) +{ + return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, + windownr, disptype, width, height); +} + + +static enum av7110_osd_palette_type bpp2pal[8] = { + Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit +}; +static osd_raw_window_t bpp2bit[8] = { + OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 +}; + +static inline int WaitUntilBmpLoaded(struct av7110 *av7110) +{ + int ret = wait_event_timeout(av7110->bmpq, + av7110->bmp_state != BMP_LOADING, 10*HZ); + if (ret == 0) { + printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", + ret, av7110->bmp_state); + av7110->bmp_state = BMP_NONE; + return -ETIMEDOUT; + } + return 0; +} + +static inline int LoadBitmap(struct av7110 *av7110, + u16 dx, u16 dy, int inc, u8 __user * data) +{ + u16 format; + int bpp; + int i; + int d, delta; + u8 c; + int ret; + + dprintk(4, "%p\n", av7110); + + format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; + + av7110->bmp_state = BMP_LOADING; + if (format == OSD_BITMAP8) { + bpp=8; delta = 1; + } else if (format == OSD_BITMAP4) { + bpp=4; delta = 2; + } else if (format == OSD_BITMAP2) { + bpp=2; delta = 4; + } else if (format == OSD_BITMAP1) { + bpp=1; delta = 8; + } else { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; + av7110->bmpp = 0; + if (av7110->bmplen > 32768) { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + for (i = 0; i < dy; i++) { + if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { + av7110->bmp_state = BMP_NONE; + return -EINVAL; + } + } + if (format != OSD_BITMAP8) { + for (i = 0; i < dx * dy / delta; i++) { + c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; + for (d = delta - 2; d >= 0; d--) { + c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] + << ((delta - d - 1) * bpp)); + ((u8 *)av7110->bmpbuf)[1024 + i] = c; + } + } + } + av7110->bmplen += 1024; + dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); + ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); + if (!ret) + ret = WaitUntilBmpLoaded(av7110); + return ret; +} + +static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) +{ + dprintk(4, "%p\n", av7110); + + return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); +} + +static inline int ReleaseBitmap(struct av7110 *av7110) +{ + dprintk(4, "%p\n", av7110); + + if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) + return -1; + if (av7110->bmp_state == BMP_LOADING) + dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); + av7110->bmp_state = BMP_NONE; + return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); +} + +static u32 RGB2YUV(u16 R, u16 G, u16 B) +{ + u16 y, u, v; + u16 Y, Cr, Cb; + + y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ + u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ + v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ + + Y = y / 256; + Cb = u / 16; + Cr = v / 16; + + return Cr | (Cb << 16) | (Y << 8); +} + +static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) +{ + int ret; + + u16 ch, cl; + u32 yuv; + + yuv = blend ? RGB2YUV(r,g,b) : 0; + cl = (yuv & 0xffff); + ch = ((yuv >> 16) & 0xffff); + ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], + color, ch, cl); + if (!ret) + ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], + color, ((blend >> 4) & 0x0f)); + return ret; +} + +static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) +{ + int i; + int length = last - first + 1; + + if (length * 4 > DATA_BUFF3_SIZE) + return -EINVAL; + + for (i = 0; i < length; i++) { + u32 color, blend, yuv; + + if (get_user(color, colors + i)) + return -EFAULT; + blend = (color & 0xF0000000) >> 4; + yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, + (color >> 16) & 0xFF) | blend : 0; + yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); + wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); + } + return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, + av7110->osdwin, + bpp2pal[av7110->osdbpp[av7110->osdwin]], + first, last); +} + +static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, + int x1, int y1, int inc, u8 __user * data) +{ + uint w, h, bpp, bpl, size, lpb, bnum, brest; + int i; + int rc,release_rc; + + w = x1 - x0 + 1; + h = y1 - y0 + 1; + if (inc <= 0) + inc = w; + if (w <= 0 || w > 720 || h <= 0 || h > 576) + return -EINVAL; + bpp = av7110->osdbpp[av7110->osdwin] + 1; + bpl = ((w * bpp + 7) & ~7) / 8; + size = h * bpl; + lpb = (32 * 1024) / bpl; + bnum = size / (lpb * bpl); + brest = size - bnum * lpb * bpl; + + if (av7110->bmp_state == BMP_LOADING) { + /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ + BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); + rc = WaitUntilBmpLoaded(av7110); + if (rc) + return rc; + /* just continue. This should work for all fw versions + * if bnum==1 && !brest && LoadBitmap was successful + */ + } + + rc = 0; + for (i = 0; i < bnum; i++) { + rc = LoadBitmap(av7110, w, lpb, inc, data); + if (rc) + break; + rc = BlitBitmap(av7110, x0, y0 + i * lpb); + if (rc) + break; + data += lpb * inc; + } + if (!rc && brest) { + rc = LoadBitmap(av7110, w, brest / bpl, inc, data); + if (!rc) + rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); + } + release_rc = ReleaseBitmap(av7110); + if (!rc) + rc = release_rc; + if (rc) + dprintk(1,"returns %d\n",rc); + return rc; +} + +int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) +{ + int ret; + + if (mutex_lock_interruptible(&av7110->osd_mutex)) + return -ERESTARTSYS; + + switch (dc->cmd) { + case OSD_Close: + ret = DestroyOSDWindow(av7110, av7110->osdwin); + break; + case OSD_Open: + av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; + ret = CreateOSDWindow(av7110, av7110->osdwin, + bpp2bit[av7110->osdbpp[av7110->osdwin]], + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); + if (ret) + break; + if (!dc->data) { + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (ret) + break; + ret = SetColorBlend(av7110, av7110->osdwin); + } + break; + case OSD_Show: + ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); + break; + case OSD_Hide: + ret = HideWindow(av7110, av7110->osdwin); + break; + case OSD_Clear: + ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); + break; + case OSD_Fill: + ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); + break; + case OSD_SetColor: + ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); + break; + case OSD_SetPalette: + if (FW_VERSION(av7110->arm_app) >= 0x2618) + ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); + else { + int i, len = dc->x0-dc->color+1; + u8 __user *colors = (u8 __user *)dc->data; + u8 r, g = 0, b = 0, blend = 0; + ret = 0; + for (i = 0; icolor + i, r, g, b, blend); + if (ret) + break; + } + } + break; + case OSD_SetPixel: + ret = DrawLine(av7110, av7110->osdwin, + dc->x0, dc->y0, 0, 0, dc->color); + break; + case OSD_SetRow: + dc->y1 = dc->y0; + fallthrough; + case OSD_SetBlock: + ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); + break; + case OSD_FillRow: + ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, + dc->x1-dc->x0+1, dc->y1, dc->color); + break; + case OSD_FillBlock: + ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); + break; + case OSD_Line: + ret = DrawLine(av7110, av7110->osdwin, + dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); + break; + case OSD_Text: + { + char textbuf[240]; + + if (strncpy_from_user(textbuf, dc->data, 240) < 0) { + ret = -EFAULT; + break; + } + textbuf[239] = 0; + if (dc->x1 > 3) + dc->x1 = 3; + ret = SetFont(av7110, av7110->osdwin, dc->x1, + (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); + if (!ret) + ret = FlushText(av7110); + if (!ret) + ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); + break; + } + case OSD_SetWindow: + if (dc->x0 < 1 || dc->x0 > 7) + ret = -EINVAL; + else { + av7110->osdwin = dc->x0; + ret = 0; + } + break; + case OSD_MoveWindow: + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (!ret) + ret = SetColorBlend(av7110, av7110->osdwin); + break; + case OSD_OpenRaw: + if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { + ret = -EINVAL; + break; + } + if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) + av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; + else + av7110->osdbpp[av7110->osdwin] = 0; + ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); + if (ret) + break; + if (!dc->data) { + ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); + if (!ret) + ret = SetColorBlend(av7110, av7110->osdwin); + } + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&av7110->osd_mutex); + if (ret==-ERESTARTSYS) + dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); + else if (ret) + dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); + + return ret; +} + +int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) +{ + switch (cap->cmd) { + case OSD_CAP_MEMSIZE: + if (FW_4M_SDRAM(av7110->arm_app)) + cap->val = 1000000; + else + cap->val = 92000; + return 0; + default: + return -EINVAL; + } +} +#endif /* CONFIG_DVB_AV7110_OSD */ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h new file mode 100644 index 000000000000..6380d8950c69 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h @@ -0,0 +1,496 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AV7110_HW_H_ +#define _AV7110_HW_H_ + +#include "av7110.h" + +/* DEBI transfer mode defs */ + +#define DEBINOSWAP 0x000e0000 +#define DEBISWAB 0x001e0000 +#define DEBISWAP 0x002e0000 + +#define ARM_WAIT_FREE (HZ) +#define ARM_WAIT_SHAKE (HZ/5) +#define ARM_WAIT_OSD (HZ) + + +enum av7110_bootstate +{ + BOOTSTATE_BUFFER_EMPTY = 0, + BOOTSTATE_BUFFER_FULL = 1, + BOOTSTATE_AV7110_BOOT_COMPLETE = 2 +}; + +enum av7110_type_rec_play_format +{ RP_None, + AudioPES, + AudioMp2, + AudioPCM, + VideoPES, + AV_PES +}; + +enum av7110_osd_palette_type +{ + NoPalet = 0, /* No palette */ + Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ + Pal2Bit = 4, /* 4 colors for 2 bit palette */ + Pal4Bit = 16, /* 16 colors for 4 bit palette */ + Pal8Bit = 256 /* 256 colors for 16 bit palette */ +}; + +/* switch defines */ +#define SB_GPIO 3 +#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ +#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ +#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ + +#define FB_GPIO 1 +#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ +#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ +#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ + +enum av7110_video_output_mode +{ + NO_OUT = 0, /* disable analog output */ + CVBS_RGB_OUT = 1, + CVBS_YC_OUT = 2, + YC_OUT = 3 +}; + +/* firmware internal msg q status: */ +#define GPMQFull 0x0001 /* Main Message Queue Full */ +#define GPMQOver 0x0002 /* Main Message Queue Overflow */ +#define HPQFull 0x0004 /* High Priority Msg Queue Full */ +#define HPQOver 0x0008 +#define OSDQFull 0x0010 /* OSD Queue Full */ +#define OSDQOver 0x0020 +#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ +#define HPQBusy 0x0080 +#define OSDQBusy 0x0100 + +/* hw section filter flags */ +#define SECTION_EIT 0x01 +#define SECTION_SINGLE 0x00 +#define SECTION_CYCLE 0x02 +#define SECTION_CONTINUOS 0x04 +#define SECTION_MODE 0x06 +#define SECTION_IPMPE 0x0C /* size up to 4k */ +#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ +#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ + +#define PBUFSIZE_NONE 0x0000 +#define PBUFSIZE_1P 0x0100 +#define PBUFSIZE_2P 0x0200 +#define PBUFSIZE_1K 0x0300 +#define PBUFSIZE_2K 0x0400 +#define PBUFSIZE_4K 0x0500 +#define PBUFSIZE_8K 0x0600 +#define PBUFSIZE_16K 0x0700 +#define PBUFSIZE_32K 0x0800 + + +/* firmware command codes */ +enum av7110_osd_command { + WCreate, + WDestroy, + WMoveD, + WMoveA, + WHide, + WTop, + DBox, + DLine, + DText, + Set_Font, + SetColor, + SetBlend, + SetWBlend, + SetCBlend, + SetNonBlend, + LoadBmp, + BlitBmp, + ReleaseBmp, + SetWTrans, + SetWNoTrans, + Set_Palette +}; + +enum av7110_pid_command { + MultiPID, + VideoPID, + AudioPID, + InitFilt, + FiltError, + NewVersion, + CacheError, + AddPIDFilter, + DelPIDFilter, + Scan, + SetDescr, + SetIR, + FlushTSQueue +}; + +enum av7110_mpeg_command { + SelAudChannels +}; + +enum av7110_audio_command { + AudioDAC, + CabADAC, + ON22K, + OFF22K, + MainSwitch, + ADSwitch, + SendDiSEqC, + SetRegister, + SpdifSwitch +}; + +enum av7110_request_command { + AudioState, + AudioBuffState, + VideoState1, + VideoState2, + VideoState3, + CrashCounter, + ReqVersion, + ReqVCXO, + ReqRegister, + ReqSecFilterError, + ReqSTC +}; + +enum av7110_encoder_command { + SetVidMode, + SetTestMode, + LoadVidCode, + SetMonitorType, + SetPanScanType, + SetFreezeMode, + SetWSSConfig +}; + +enum av7110_rec_play_state { + __Record, + __Stop, + __Play, + __Pause, + __Slow, + __FF_IP, + __Scan_I, + __Continue +}; + +enum av7110_fw_cmd_misc { + AV7110_FW_VIDEO_ZOOM = 1, + AV7110_FW_VIDEO_COMMAND, + AV7110_FW_AUDIO_COMMAND +}; + +enum av7110_command_type { + COMTYPE_NOCOM, + COMTYPE_PIDFILTER, + COMTYPE_MPEGDECODER, + COMTYPE_OSD, + COMTYPE_BMP, + COMTYPE_ENCODER, + COMTYPE_AUDIODAC, + COMTYPE_REQUEST, + COMTYPE_SYSTEM, + COMTYPE_REC_PLAY, + COMTYPE_COMMON_IF, + COMTYPE_PID_FILTER, + COMTYPE_PES, + COMTYPE_TS, + COMTYPE_VIDEO, + COMTYPE_AUDIO, + COMTYPE_CI_LL, + COMTYPE_MISC = 0x80 +}; + +#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ +#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ +#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ +#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ +#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ + +/* MPEG video decoder commands */ +#define AV_VIDEO_CMD_STOP 0x000e +#define AV_VIDEO_CMD_PLAY 0x000d +#define AV_VIDEO_CMD_FREEZE 0x0102 +#define AV_VIDEO_CMD_FFWD 0x0016 +#define AV_VIDEO_CMD_SLOW 0x0022 + +/* MPEG audio decoder commands */ +#define AUDIO_CMD_MUTE 0x0001 +#define AUDIO_CMD_UNMUTE 0x0002 +#define AUDIO_CMD_PCM16 0x0010 +#define AUDIO_CMD_STEREO 0x0080 +#define AUDIO_CMD_MONO_L 0x0100 +#define AUDIO_CMD_MONO_R 0x0200 +#define AUDIO_CMD_SYNC_OFF 0x000e +#define AUDIO_CMD_SYNC_ON 0x000f + +/* firmware data interface codes */ +#define DATA_NONE 0x00 +#define DATA_FSECTION 0x01 +#define DATA_IPMPE 0x02 +#define DATA_MPEG_RECORD 0x03 +#define DATA_DEBUG_MESSAGE 0x04 +#define DATA_COMMON_INTERFACE 0x05 +#define DATA_MPEG_PLAY 0x06 +#define DATA_BMP_LOAD 0x07 +#define DATA_IRCOMMAND 0x08 +#define DATA_PIPING 0x09 +#define DATA_STREAMING 0x0a +#define DATA_CI_GET 0x0b +#define DATA_CI_PUT 0x0c +#define DATA_MPEG_VIDEO_EVENT 0x0d + +#define DATA_PES_RECORD 0x10 +#define DATA_PES_PLAY 0x11 +#define DATA_TS_RECORD 0x12 +#define DATA_TS_PLAY 0x13 + +/* ancient CI command codes, only two are actually still used + * by the link level CI firmware */ +#define CI_CMD_ERROR 0x00 +#define CI_CMD_ACK 0x01 +#define CI_CMD_SYSTEM_READY 0x02 +#define CI_CMD_KEYPRESS 0x03 +#define CI_CMD_ON_TUNED 0x04 +#define CI_CMD_ON_SWITCH_PROGRAM 0x05 +#define CI_CMD_SECTION_ARRIVED 0x06 +#define CI_CMD_SECTION_TIMEOUT 0x07 +#define CI_CMD_TIME 0x08 +#define CI_CMD_ENTER_MENU 0x09 +#define CI_CMD_FAST_PSI 0x0a +#define CI_CMD_GET_SLOT_INFO 0x0b + +#define CI_MSG_NONE 0x00 +#define CI_MSG_CI_INFO 0x01 +#define CI_MSG_MENU 0x02 +#define CI_MSG_LIST 0x03 +#define CI_MSG_TEXT 0x04 +#define CI_MSG_REQUEST_INPUT 0x05 +#define CI_MSG_INPUT_COMPLETE 0x06 +#define CI_MSG_LIST_MORE 0x07 +#define CI_MSG_MENU_MORE 0x08 +#define CI_MSG_CLOSE_MMI_IMM 0x09 +#define CI_MSG_SECTION_REQUEST 0x0a +#define CI_MSG_CLOSE_FILTER 0x0b +#define CI_PSI_COMPLETE 0x0c +#define CI_MODULE_READY 0x0d +#define CI_SWITCH_PRG_REPLY 0x0e +#define CI_MSG_TEXT_MORE 0x0f + +#define CI_MSG_CA_PMT 0xe0 +#define CI_MSG_ERROR 0xf0 + + +/* base address of the dual ported RAM which serves as communication + * area between PCI bus and av7110, + * as seen by the DEBI bus of the saa7146 */ +#define DPRAM_BASE 0x4000 + +/* boot protocol area */ +#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) +#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) +#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) +#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) +#define AV7110_BOOT_MAX_SIZE 0xc00 + +/* firmware command protocol area */ +#define IRQ_STATE (DPRAM_BASE + 0x0F4) +#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) +#define MSGSTATE (DPRAM_BASE + 0x0F8) +#define COMMAND (DPRAM_BASE + 0x0FC) +#define COM_BUFF (DPRAM_BASE + 0x100) +#define COM_BUFF_SIZE 0x20 + +/* various data buffers */ +#define BUFF1_BASE (DPRAM_BASE + 0x120) +#define BUFF1_SIZE 0xE0 + +#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) +#define DATA_BUFF0_SIZE 0x0800 + +#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) +#define DATA_BUFF1_SIZE 0x0800 + +#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) +#define DATA_BUFF2_SIZE 0x0800 + +#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) +#define DATA_BUFF3_SIZE 0x0400 + +#define Reserved (DPRAM_BASE + 0x1E00) +#define Reserved_SIZE 0x1C0 + + +/* firmware status area */ +#define STATUS_BASE (DPRAM_BASE + 0x1FC0) +#define STATUS_LOOPS (STATUS_BASE + 0x08) + +#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) +/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ +#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) + +/* firmware data protocol area */ +#define RX_TYPE (DPRAM_BASE + 0x1FE8) +#define RX_LEN (DPRAM_BASE + 0x1FEA) +#define TX_TYPE (DPRAM_BASE + 0x1FEC) +#define TX_LEN (DPRAM_BASE + 0x1FEE) + +#define RX_BUFF (DPRAM_BASE + 0x1FF4) +#define TX_BUFF (DPRAM_BASE + 0x1FF6) + +#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) +#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) + +#define IRQ_RX (DPRAM_BASE + 0x1FFC) +#define IRQ_TX (DPRAM_BASE + 0x1FFE) + +/* used by boot protocol to load firmware into av7110 DRAM */ +#define DRAM_START_CODE 0x2e000404 +#define DRAM_MAX_CODE_SIZE 0x00100000 + +/* saa7146 gpio lines */ +#define RESET_LINE 2 +#define DEBI_DONE_LINE 1 +#define ARM_IRQ_LINE 0 + + + +extern int av7110_bootarm(struct av7110 *av7110); +extern int av7110_firmversion(struct av7110 *av7110); +#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) +#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) +#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) + +extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); +extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); +extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, + int request_buf_len, u16 *reply_buf, int reply_buf_len); + + +/* DEBI (saa7146 data extension bus interface) access */ +extern int av7110_debiwrite(struct av7110 *av7110, u32 config, + int addr, u32 val, unsigned int count); +extern u32 av7110_debiread(struct av7110 *av7110, u32 config, + int addr, unsigned int count); + + +/* DEBI during interrupt */ +/* single word writes */ +static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) +{ + av7110_debiwrite(av7110, config, addr, val, count); +} + +/* buffer writes */ +static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, + const u8 *val, int count) +{ + memcpy(av7110->debi_virt, val, count); + av7110_debiwrite(av7110, config, addr, 0, count); +} + +static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) +{ + u32 res; + + res=av7110_debiread(av7110, config, addr, count); + if (count<=4) + memcpy(av7110->debi_virt, (char *) &res, count); + return res; +} + +/* DEBI outside interrupts, only for count <= 4! */ +static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) +{ + unsigned long flags; + + spin_lock_irqsave(&av7110->debilock, flags); + av7110_debiwrite(av7110, config, addr, val, count); + spin_unlock_irqrestore(&av7110->debilock, flags); +} + +static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) +{ + unsigned long flags; + u32 res; + + spin_lock_irqsave(&av7110->debilock, flags); + res=av7110_debiread(av7110, config, addr, count); + spin_unlock_irqrestore(&av7110->debilock, flags); + return res; +} + +/* handle mailbox registers of the dual ported RAM */ +static inline void ARM_ResetMailBox(struct av7110 *av7110) +{ + unsigned long flags; + + spin_lock_irqsave(&av7110->debilock, flags); + av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); + av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); + spin_unlock_irqrestore(&av7110->debilock, flags); +} + +static inline void ARM_ClearMailBox(struct av7110 *av7110) +{ + iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +} + +static inline void ARM_ClearIrq(struct av7110 *av7110) +{ + irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +} + +/**************************************************************************** + * Firmware commands + ****************************************************************************/ + +static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) +{ + return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); +} + +static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) +{ + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); +} + +static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) +{ + return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, + (com>>16), (com&0xffff), + (arg>>16), (arg&0xffff)); +} + +static inline int audcom(struct av7110 *av7110, u32 com) +{ + return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, + (com>>16), (com&0xffff)); +} + +static inline int Set22K(struct av7110 *av7110, int state) +{ + return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); +} + + +extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); + + +#ifdef CONFIG_DVB_AV7110_OSD +extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); +extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); +#endif /* CONFIG_DVB_AV7110_OSD */ + + + +#endif /* _AV7110_HW_H_ */ diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c new file mode 100644 index 000000000000..30330ed01ce8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "dvb_filter.h" +#include "av7110_ipack.h" +#include /* for memcpy() */ +#include + + +void av7110_ipack_reset(struct ipack *p) +{ + p->found = 0; + p->cid = 0; + p->plength = 0; + p->flag1 = 0; + p->flag2 = 0; + p->hlength = 0; + p->mpeg = 0; + p->check = 0; + p->which = 0; + p->done = 0; + p->count = 0; +} + + +int av7110_ipack_init(struct ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)) +{ + if (!(p->buf = vmalloc(size))) { + printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); + return -ENOMEM; + } + p->size = size; + p->func = func; + p->repack_subids = 0; + av7110_ipack_reset(p); + return 0; +} + + +void av7110_ipack_free(struct ipack *p) +{ + vfree(p->buf); +} + + +static void send_ipack(struct ipack *p) +{ + int off; + struct dvb_audio_info ai; + int ac3_off = 0; + int streamid = 0; + int nframes = 0; + int f = 0; + + switch (p->mpeg) { + case 2: + if (p->count < 10) + return; + p->buf[3] = p->cid; + p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); + p->buf[5] = (u8)((p->count - 6) & 0x00ff); + if (p->repack_subids && p->cid == PRIVATE_STREAM1) { + off = 9 + p->buf[8]; + streamid = p->buf[off]; + if ((streamid & 0xf8) == 0x80) { + ai.off = 0; + ac3_off = ((p->buf[off + 2] << 8)| + p->buf[off + 3]); + if (ac3_off < p->count) + f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, + p->count - ac3_off, &ai, 0); + if (!f) { + nframes = (p->count - off - 3 - ac3_off) / + ai.framesize + 1; + p->buf[off + 2] = (ac3_off >> 8) & 0xff; + p->buf[off + 3] = (ac3_off) & 0xff; + p->buf[off + 1] = nframes; + ac3_off += nframes * ai.framesize - p->count; + } + } + } + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x80; + p->buf[7] = 0x00; + p->buf[8] = 0x00; + p->count = 9; + if (p->repack_subids && p->cid == PRIVATE_STREAM1 + && (streamid & 0xf8) == 0x80) { + p->count += 4; + p->buf[9] = streamid; + p->buf[10] = (ac3_off >> 8) & 0xff; + p->buf[11] = (ac3_off) & 0xff; + p->buf[12] = 0; + } + break; + + case 1: + if (p->count < 8) + return; + p->buf[3] = p->cid; + p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); + p->buf[5] = (u8)((p->count - 6) & 0x00ff); + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x0f; + p->count = 7; + break; + } +} + + +void av7110_ipack_flush(struct ipack *p) +{ + if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) + return; + p->plength = p->found - 6; + p->found = 0; + send_ipack(p); + av7110_ipack_reset(p); +} + + +static void write_ipack(struct ipack *p, const u8 *data, int count) +{ + u8 headr[3] = { 0x00, 0x00, 0x01 }; + + if (p->count < 6) { + memcpy(p->buf, headr, 3); + p->count = 6; + } + + if (p->count + count < p->size){ + memcpy(p->buf+p->count, data, count); + p->count += count; + } else { + int rest = p->size - p->count; + memcpy(p->buf+p->count, data, rest); + p->count += rest; + send_ipack(p); + if (count - rest > 0) + write_ipack(p, data + rest, count - rest); + } +} + + +int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) +{ + int l; + int c = 0; + + while (c < count && (p->mpeg == 0 || + (p->mpeg == 1 && p->found < 7) || + (p->mpeg == 2 && p->found < 9)) + && (p->found < 5 || !p->done)) { + switch (p->found) { + case 0: + case 1: + if (buf[c] == 0x00) + p->found++; + else + p->found = 0; + c++; + break; + case 2: + if (buf[c] == 0x01) + p->found++; + else if (buf[c] == 0) + p->found = 2; + else + p->found = 0; + c++; + break; + case 3: + p->cid = 0; + switch (buf[c]) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + p->done = 1; + fallthrough; + case PRIVATE_STREAM1: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + p->found++; + p->cid = buf[c]; + c++; + break; + default: + p->found = 0; + break; + } + break; + + case 4: + if (count-c > 1) { + p->plen[0] = buf[c]; + c++; + p->plen[1] = buf[c]; + c++; + p->found += 2; + p->plength = (p->plen[0] << 8) | p->plen[1]; + } else { + p->plen[0] = buf[c]; + p->found++; + return count; + } + break; + case 5: + p->plen[1] = buf[c]; + c++; + p->found++; + p->plength = (p->plen[0] << 8) | p->plen[1]; + break; + case 6: + if (!p->done) { + p->flag1 = buf[c]; + c++; + p->found++; + if ((p->flag1 & 0xc0) == 0x80) + p->mpeg = 2; + else { + p->hlength = 0; + p->which = 0; + p->mpeg = 1; + p->flag2 = 0; + } + } + break; + + case 7: + if (!p->done && p->mpeg == 2) { + p->flag2 = buf[c]; + c++; + p->found++; + } + break; + + case 8: + if (!p->done && p->mpeg == 2) { + p->hlength = buf[c]; + c++; + p->found++; + } + break; + } + } + + if (c == count) + return count; + + if (!p->plength) + p->plength = MMAX_PLENGTH - 6; + + if (p->done || ((p->mpeg == 2 && p->found >= 9) || + (p->mpeg == 1 && p->found >= 7))) { + switch (p->cid) { + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case PRIVATE_STREAM1: + if (p->mpeg == 2 && p->found == 9) { + write_ipack(p, &p->flag1, 1); + write_ipack(p, &p->flag2, 1); + write_ipack(p, &p->hlength, 1); + } + + if (p->mpeg == 1 && p->found == 7) + write_ipack(p, &p->flag1, 1); + + if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && + p->found < 14) { + while (c < count && p->found < 14) { + p->pts[p->found - 9] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + } + if (c == count) + return count; + } + + if (p->mpeg == 1 && p->which < 2000) { + + if (p->found == 7) { + p->check = p->flag1; + p->hlength = 1; + } + + while (!p->which && c < count && + p->check == 0xff){ + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + } + + if (c == count) + return count; + + if ((p->check & 0xc0) == 0x40 && !p->which) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + + p->which = 1; + if (c == count) + return count; + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if (c == count) + return count; + } + + if (p->which == 1) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if (c == count) + return count; + } + + if ((p->check & 0x30) && p->check != 0xff) { + p->flag2 = (p->check & 0xf0) << 2; + p->pts[0] = p->check; + p->which = 3; + } + + if (c == count) + return count; + if (p->which > 2){ + if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { + while (c < count && p->which < 7) { + p->pts[p->which - 2] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if (c == count) + return count; + } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { + while (c < count && p->which < 12) { + if (p->which < 7) + p->pts[p->which - 2] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if (c == count) + return count; + } + p->which = 2000; + } + + } + + while (c < count && p->found < p->plength + 6) { + l = count - c; + if (l + p->found > p->plength + 6) + l = p->plength + 6 - p->found; + write_ipack(p, buf + c, l); + p->found += l; + c += l; + } + break; + } + + + if (p->done) { + if (p->found + count - c < p->plength + 6) { + p->found += count - c; + c = count; + } else { + c += p->plength + 6 - p->found; + p->found = p->plength + 6; + } + } + + if (p->plength && p->found == p->plength + 6) { + send_ipack(p); + av7110_ipack_reset(p); + if (c < count) + av7110_ipack_instant_repack(buf + c, count - c, p); + } + } + return count; +} diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h new file mode 100644 index 000000000000..943ec899bb93 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AV7110_IPACK_H_ +#define _AV7110_IPACK_H_ + +extern int av7110_ipack_init(struct ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)); + +extern void av7110_ipack_reset(struct ipack *p); +extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); +extern void av7110_ipack_free(struct ipack * p); +extern void av7110_ipack_flush(struct ipack *p); + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c new file mode 100644 index 000000000000..a851ba328e4a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the remote control of SAA7146 based AV7110 cards + * + * Copyright (C) 1999-2003 Holger Waechtler + * Copyright (C) 2003-2007 Oliver Endriss + * Copyright (C) 2019 Sean Young + */ + +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" + +#define IR_RC5 0 +#define IR_RCMM 1 +#define IR_RC5_EXT 2 /* internal only */ + +/* interrupt handler */ +void av7110_ir_handler(struct av7110 *av7110, u32 ircom) +{ + struct rc_dev *rcdev = av7110->ir.rcdev; + enum rc_proto proto; + u32 command, addr, scancode; + u32 toggle; + + dprintk(4, "ir command = %08x\n", ircom); + + if (rcdev) { + switch (av7110->ir.ir_config) { + case IR_RC5: /* RC5: 5 bits device address, 6 bits command */ + command = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + scancode = RC_SCANCODE_RC5(addr, command); + toggle = ircom & 0x0800; + proto = RC_PROTO_RC5; + break; + + case IR_RCMM: /* RCMM: 32 bits scancode */ + scancode = ircom & ~0x8000; + toggle = ircom & 0x8000; + proto = RC_PROTO_RCMM32; + break; + + case IR_RC5_EXT: + /* + * extended RC5: 5 bits device address, 7 bits command + * + * Extended RC5 uses only one start bit. The second + * start bit is re-assigned bit 6 of the command bit. + */ + command = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + if (!(ircom & 0x1000)) + command |= 0x40; + scancode = RC_SCANCODE_RC5(addr, command); + toggle = ircom & 0x0800; + proto = RC_PROTO_RC5; + break; + default: + dprintk(2, "unknown ir config %d\n", + av7110->ir.ir_config); + return; + } + + rc_keydown(rcdev, proto, scancode, toggle != 0); + } +} + +int av7110_set_ir_config(struct av7110 *av7110) +{ + dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); + + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, + av7110->ir.ir_config); +} + +static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) +{ + struct av7110 *av7110 = rcdev->priv; + u32 ir_config; + + if (*rc_type & RC_PROTO_BIT_RCMM32) { + ir_config = IR_RCMM; + *rc_type = RC_PROTO_BIT_RCMM32; + } else if (*rc_type & RC_PROTO_BIT_RC5) { + if (FW_VERSION(av7110->arm_app) >= 0x2620) + ir_config = IR_RC5_EXT; + else + ir_config = IR_RC5; + *rc_type = RC_PROTO_BIT_RC5; + } else { + return -EINVAL; + } + + if (ir_config == av7110->ir.ir_config) + return 0; + + av7110->ir.ir_config = ir_config; + + return av7110_set_ir_config(av7110); +} + +int av7110_ir_init(struct av7110 *av7110) +{ + struct rc_dev *rcdev; + struct pci_dev *pci; + int ret; + + rcdev = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!rcdev) + return -ENOMEM; + + pci = av7110->dev->pci; + + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), + "pci-%s/ir0", pci_name(pci)); + + rcdev->device_name = av7110->card_name; + rcdev->driver_name = KBUILD_MODNAME; + rcdev->input_phys = av7110->ir.input_phys; + rcdev->input_id.bustype = BUS_PCI; + rcdev->input_id.version = 2; + if (pci->subsystem_vendor) { + rcdev->input_id.vendor = pci->subsystem_vendor; + rcdev->input_id.product = pci->subsystem_device; + } else { + rcdev->input_id.vendor = pci->vendor; + rcdev->input_id.product = pci->device; + } + + rcdev->dev.parent = &pci->dev; + rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32; + rcdev->change_protocol = change_protocol; + rcdev->map_name = RC_MAP_HAUPPAUGE; + rcdev->priv = av7110; + + av7110->ir.rcdev = rcdev; + av7110->ir.ir_config = IR_RC5; + av7110_set_ir_config(av7110); + + ret = rc_register_device(rcdev); + if (ret) { + av7110->ir.rcdev = NULL; + rc_free_device(rcdev); + } + + return ret; +} + +void av7110_ir_exit(struct av7110 *av7110) +{ + rc_unregister_device(av7110->ir.rcdev); +} + +//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); +//MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c new file mode 100644 index 000000000000..c89f536f699c --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c @@ -0,0 +1,952 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss + * + * the project's page is at https://linuxtv.org + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" + +int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) +{ + u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; + struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; + + switch (av7110->adac_type) { + case DVB_ADAC_MSP34x0: + msgs.addr = 0x40; + break; + case DVB_ADAC_MSP34x5: + msgs.addr = 0x42; + break; + default: + return 0; + } + + if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { + dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", + av7110->dvb_adapter.num, reg, val); + return -EIO; + } + return 0; +} + +static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) +{ + u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; + u8 msg2[2]; + struct i2c_msg msgs[2] = { + { .flags = 0 , .len = 3, .buf = msg1 }, + { .flags = I2C_M_RD, .len = 2, .buf = msg2 } + }; + + switch (av7110->adac_type) { + case DVB_ADAC_MSP34x0: + msgs[0].addr = 0x40; + msgs[1].addr = 0x40; + break; + case DVB_ADAC_MSP34x5: + msgs[0].addr = 0x42; + msgs[1].addr = 0x42; + break; + default: + return 0; + } + + if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { + dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", + av7110->dvb_adapter.num, reg); + return -EIO; + } + *val = (msg2[0] << 8) | msg2[1]; + return 0; +} + +static struct v4l2_input inputs[4] = { + { + .index = 0, + .name = "DVB", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 1, + .tuner = 0, /* ignored */ + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 1, + .name = "Television", + .type = V4L2_INPUT_TYPE_TUNER, + .audioset = 1, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 2, + .name = "Video", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + }, { + .index = 3, + .name = "Y/C", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .status = 0, + .capabilities = V4L2_IN_CAP_STD, + } +}; + +static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) +{ + struct av7110 *av7110 = dev->ext_priv; + u8 buf[] = { 0x00, reg, data }; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; + + dprintk(4, "dev: %p\n", dev); + + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + return -1; + return 0; +} + +static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) +{ + struct av7110 *av7110 = dev->ext_priv; + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; + + dprintk(4, "dev: %p\n", dev); + + if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + return -1; + return 0; +} + +static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + u32 div; + u8 config; + u8 buf[4]; + + dprintk(4, "freq: 0x%08x\n", freq); + + /* magic number: 614. tuning with the frequency given by v4l2 + is always off by 614*62.5 = 38375 kHz...*/ + div = freq + 614; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x8e; + + if (freq < 16U * 16825 / 100) + config = 0xa0; + else if (freq < 16U * 44725 / 100) + config = 0x90; + else + config = 0x30; + config &= ~0x02; + + buf[3] = config; + + return tuner_write(dev, 0x61, buf); +} + +static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) +{ + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + u32 div; + u8 data[4]; + + div = (freq + 38900000 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xce; + + if (freq < 45000000) + return -EINVAL; + else if (freq < 137000000) + data[3] = 0x01; + else if (freq < 403000000) + data[3] = 0x02; + else if (freq < 860000000) + data[3] = 0x04; + else + return -EINVAL; + + if (av7110->fe->ops.i2c_gate_ctrl) + av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); + return tuner_write(dev, 0x63, data); +} + + + +static struct saa7146_standard analog_standard[]; +static struct saa7146_standard dvb_standard[]; +static struct saa7146_standard standard[]; + +static const struct v4l2_audio msp3400_v4l2_audio = { + .index = 0, + .name = "Television", + .capability = V4L2_AUDCAP_STEREO +}; + +static int av7110_dvb_c_switch(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + u16 adswitch; + int source, sync, err; + + dprintk(4, "%p\n", av7110); + + if ((vv->video_status & STATUS_OVERLAY) != 0) { + vv->ov_suspend = vv->video_fh; + err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ + if (err != 0) { + dprintk(2, "suspending video failed\n"); + vv->ov_suspend = NULL; + } + } + + if (0 != av7110->current_input) { + dprintk(1, "switching to analog TV:\n"); + adswitch = 1; + source = SAA7146_HPS_SOURCE_PORT_B; + sync = SAA7146_HPS_SYNC_PORT_B; + memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); + + switch (av7110->current_input) { + case 1: + dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) + } + if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + case 2: + dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); + if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + case 3: + dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); + if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) + dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); + break; + default: + dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); + } + } else { + adswitch = 0; + source = SAA7146_HPS_SOURCE_PORT_A; + sync = SAA7146_HPS_SYNC_PORT_A; + memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); + dprintk(1, "switching DVB mode\n"); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + } + } + + /* hmm, this does not do anything!? */ + if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) + dprintk(1, "ADSwitch error\n"); + + saa7146_set_hps_source_and_sync(dev, source, sync); + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + u16 stereo_det; + s8 stereo; + + dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); + + if (!av7110->analog_tuner_flags || t->index != 0) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strscpy((char *)t->name, "Television", sizeof(t->name)); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ + t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ + /* FIXME: add the real signal strength here */ + t->signal = 0xffff; + t->afc = 0; + + /* FIXME: standard / stereo detection is still broken */ + msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); + dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); + msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); + dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); + stereo = (s8)(stereo_det >> 8); + if (stereo > 0x10) { + /* stereo */ + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + t->audmode = V4L2_TUNER_MODE_STEREO; + } else if (stereo < -0x10) { + /* bilingual */ + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + t->audmode = V4L2_TUNER_MODE_LANG1; + } else /* mono */ + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + u16 fm_matrix, src; + dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); + fm_matrix = 0x3001; /* stereo */ + src = 0x0020; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); + fm_matrix = 0x3000; /* bilingual */ + src = 0x0020; + break; + case V4L2_TUNER_MODE_LANG1: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0000; + break; + case V4L2_TUNER_MODE_LANG2: + dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0010; + break; + default: /* case V4L2_TUNER_MODE_MONO: */ + dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); + fm_matrix = 0x3000; /* mono */ + src = 0x0030; + break; + } + msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); + msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); + msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); + msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = av7110->current_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); + + if (!av7110->analog_tuner_flags || av7110->current_input != 1) + return -EINVAL; + + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); + + /* tune in desired frequency */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) + ves1820_set_tv_freq(dev, f->frequency); + else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) + stv0297_set_tv_freq(dev, f->frequency); + av7110->current_freq = f->frequency; + + msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ + msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); + + if (av7110->analog_tuner_flags) { + if (i->index >= 4) + return -EINVAL; + } else { + if (i->index != 0) + return -EINVAL; + } + + memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + *input = av7110->current_input; + dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_INPUT: %d\n", input); + + if (!av7110->analog_tuner_flags) + return input ? -EINVAL : 0; + + if (input >= 4) + return -EINVAL; + + av7110->current_input = input; + return av7110_dvb_c_switch(fh); +} + +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + *a = msp3400_v4l2_audio; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + if (av7110->current_input >= 2) + return -EINVAL; + *a = msp3400_v4l2_audio; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); + if (av7110->current_input >= 2) + return -EINVAL; + return a->index ? -EINVAL : 0; +} + +static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_sliced_vbi_cap *cap) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); + if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return -EINVAL; + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + cap->service_set = V4L2_SLICED_WSS_625; + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + } + return 0; +} + +static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_FMT:\n"); + if (FW_VERSION(av7110->arm_app) < 0x2623) + return -EINVAL; + memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); + if (av7110->wssMode) { + f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; + f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); + } + return 0; +} + +static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_S_FMT\n"); + if (FW_VERSION(av7110->arm_app) < 0x2623) + return -EINVAL; + if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && + f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { + memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); + /* WSS controlled by firmware */ + av7110->wssMode = 0; + av7110->wssData = 0; + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, + SetWSSConfig, 1, 0); + } else { + memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); + f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; + f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); + /* WSS controlled by userspace */ + av7110->wssMode = 1; + av7110->wssData = 0; + } + return 0; +} + +static int av7110_vbi_reset(struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + + dprintk(2, "%s\n", __func__); + av7110->wssMode = 0; + av7110->wssData = 0; + if (FW_VERSION(av7110->arm_app) < 0x2623) + return 0; + else + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); +} + +static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + struct v4l2_sliced_vbi_data d; + int rc; + + dprintk(2, "%s\n", __func__); + if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) + return -EINVAL; + if (copy_from_user(&d, data, count)) + return -EFAULT; + if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) + return -EINVAL; + if (d.id) + av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; + else + av7110->wssData = 0x8000; + rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); + return (rc < 0) ? rc : count; +} + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + +static u8 saa7113_init_regs[] = { + 0x02, 0xd0, + 0x03, 0x23, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xe9, + 0x07, 0x0d, + 0x08, 0x98, + 0x09, 0x02, + 0x0a, 0x80, + 0x0b, 0x40, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x7c, + 0x10, 0x48, + 0x11, 0x0c, + 0x12, 0x8b, + 0x13, 0x1a, + 0x14, 0x00, + 0x15, 0x00, + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1b, 0x00, + 0x1c, 0x00, + 0x1d, 0x00, + 0x1e, 0x00, + + 0x41, 0x77, + 0x42, 0x77, + 0x43, 0x77, + 0x44, 0x77, + 0x45, 0x77, + 0x46, 0x77, + 0x47, 0x77, + 0x48, 0x77, + 0x49, 0x77, + 0x4a, 0x77, + 0x4b, 0x77, + 0x4c, 0x77, + 0x4d, 0x77, + 0x4e, 0x77, + 0x4f, 0x77, + 0x50, 0x77, + 0x51, 0x77, + 0x52, 0x77, + 0x53, 0x77, + 0x54, 0x77, + 0x55, 0x77, + 0x56, 0x77, + 0x57, 0xff, + + 0xff +}; + + +static struct saa7146_ext_vv av7110_vv_data_st; +static struct saa7146_ext_vv av7110_vv_data_c; + +int av7110_init_analog_module(struct av7110 *av7110) +{ + u16 version1, version2; + + if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && + i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { + pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_MSP34x0; + } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && + i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { + pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", + av7110->dvb_adapter.num); + av7110->adac_type = DVB_ADAC_MSP34x5; + } else + return -ENODEV; + + msleep(100); // the probing above resets the msp... + msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); + msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); + dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", + av7110->dvb_adapter.num, version1, version2); + msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone + msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source + msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source + msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume + msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source + msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume + msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART + + if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { + pr_info("saa7113 not accessible\n"); + } else { + u8 *i = saa7113_init_regs; + + if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { + /* Fujitsu/Siemens DVB-Cable */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { + /* Hauppauge/TT DVB-C premium */ + av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; + } + + /* setup for DVB by default */ + if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { + if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) + dprintk(1, "setting band in demodulator failed\n"); + } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { + saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) + saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) + } + + /* init the saa7113 */ + while (*i != 0xff) { + if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { + dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); + break; + } + i += 2; + } + /* setup msp for analog sound: B/G Dual-FM */ + msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 + msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG + msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz + msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI + msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz + msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI + msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 + } + + memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); + /* set dd1 stream a & b */ + saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); + saa7146_write(av7110->dev, DD1_INIT, 0x03000700); + saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + return 0; +} + +int av7110_init_v4l(struct av7110 *av7110) +{ + struct saa7146_dev* dev = av7110->dev; + struct saa7146_ext_vv *vv_data; + int ret; + + /* special case DVB-C: these cards have an analog tuner + plus need some special handling, so we have separate + saa7146_ext_vv data for these... */ + if (av7110->analog_tuner_flags) + vv_data = &av7110_vv_data_c; + else + vv_data = &av7110_vv_data_st; + ret = saa7146_vv_init(dev, vv_data); + + if (ret) { + ERR("cannot init capture device. skipping\n"); + return -ENODEV; + } + vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data->vid_ops.vidioc_g_input = vidioc_g_input; + vv_data->vid_ops.vidioc_s_input = vidioc_s_input; + vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; + + vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; + vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; + vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; + vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + + if (FW_VERSION(av7110->arm_app) < 0x2623) + vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; + + if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_VIDEO)) { + ERR("cannot register capture device. skipping\n"); + saa7146_vv_release(dev); + return -ENODEV; + } + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) + ERR("cannot register vbi v4l2 device. skipping\n"); + } + return 0; +} + +int av7110_exit_v4l(struct av7110 *av7110) +{ + struct saa7146_dev* dev = av7110->dev; + + saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); + saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); + + saa7146_vv_release(dev); + + return 0; +} + + + +/* FIXME: these values are experimental values that look better than the + values from the latest "official" driver -- at least for me... (MiHu) */ +static struct saa7146_standard standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x15, .v_field = 288, + .h_offset = 0x48, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static struct saa7146_standard analog_standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x1b, .v_field = 288, + .h_offset = 0x08, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static struct saa7146_standard dvb_standard[] = { + { + .name = "PAL", .id = V4L2_STD_PAL_BG, + .v_offset = 0x14, .v_field = 288, + .h_offset = 0x48, .h_pixels = 708, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x10, .v_field = 244, + .h_offset = 0x40, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + } +}; + +static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +{ + struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + + if (std->id & V4L2_STD_PAL) { + av7110->vidmode = AV7110_VIDEO_MODE_PAL; + av7110_set_vidmode(av7110, av7110->vidmode); + } + else if (std->id & V4L2_STD_NTSC) { + av7110->vidmode = AV7110_VIDEO_MODE_NTSC; + av7110_set_vidmode(av7110, av7110->vidmode); + } + else + return -1; + + return 0; +} + + +static struct saa7146_ext_vv av7110_vv_data_st = { + .inputs = 1, + .audios = 1, + .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, + .flags = 0, + + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, + + .vbi_fops.open = av7110_vbi_reset, + .vbi_fops.release = av7110_vbi_reset, + .vbi_fops.write = av7110_vbi_write, +}; + +static struct saa7146_ext_vv av7110_vv_data_c = { + .inputs = 1, + .audios = 1, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, + .flags = SAA7146_USE_PORT_B_FOR_VBI, + + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, + + .vbi_fops.open = av7110_vbi_reset, + .vbi_fops.release = av7110_vbi_reset, + .vbi_fops.write = av7110_vbi_write, +}; + diff --git a/drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c b/drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c new file mode 100644 index 000000000000..d173c8ade6a7 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-patch.c: driver for Budget Patch, + * hardware modification of DVB-S cards enabling full TS + * + * Written by Emard + * + * Original idea by Roberto Deza + * + * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic + * and Metzlerbros + * + * the project's page is at https://linuxtv.org + */ + +#include "av7110.h" +#include "av7110_hw.h" +#include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "tda8083.h" + +#include "bsru6.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define budget_patch budget + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); +//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), +// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), + { + .vendor = 0, + } +}; + +/* those lines are for budget-patch to be tried +** on a true budget card and observe the +** behaviour of VSYNC generated by rps1. +** this code was shamelessly copy/pasted from budget.c +*/ +static void gpio_Set22K (struct budget *budget, int state) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +} + +/* Diseqc functions only for TT Budget card */ +/* taken from the Skyvision DVB driver by + Ralph Metzler */ + +static void DiseqcSendBit (struct budget *budget, int data) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + udelay(data ? 500 : 1000); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + udelay(data ? 1000 : 500); +} + +static void DiseqcSendByte (struct budget *budget, int data) +{ + int i, par=1, d; + + dprintk(2, "budget: %p\n", budget); + + for (i=7; i>=0; i--) { + d = (data>>i)&1; + par ^= d; + DiseqcSendBit(budget, d); + } + + DiseqcSendBit(budget, par); +} + +static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +{ + struct saa7146_dev *dev=budget->dev; + int i; + + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + mdelay(16); + + for (i=0; idvb->priv; + + switch (tone) { + case SEC_TONE_ON: + gpio_Set22K (budget, 1); + break; + + case SEC_TONE_OFF: + gpio_Set22K (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, 0, NULL, minicmd); + + return 0; +} + +static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) +{ + int i; + + dprintk(2, "budget: %p\n", budget); + + for (i = 2; i < length; i++) + { + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); + msleep(5); + } + if (length) + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); + else + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); + msleep(5); + ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); + msleep(5); + return 0; +} + +static void av7110_set22k(struct budget_patch *budget, int state) +{ + u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; + + dprintk(2, "budget: %p\n", budget); + budget_av7110_send_fw_cmd(budget, buf, 2); +} + +static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) +{ + int i; + u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + dprintk(2, "budget: %p\n", budget); + + if (len>10) + len=10; + + buf[1] = len+2; + buf[2] = len; + + if (burst != -1) + buf[3]=burst ? 0x01 : 0x00; + else + buf[3]=0xffff; + + for (i=0; idvb->priv; + + switch (tone) { + case SEC_TONE_ON: + av7110_set22k (budget, 1); + break; + + case SEC_TONE_OFF: + av7110_set22k (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + + av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_patch_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + + av7110_send_diseqc_msg (budget, 0, NULL, minicmd); + + return 0; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (p->frequency + 479500) / 125; + + if (p->frequency > 2000000) + pwr = 3; + else if (p->frequency > 1800000) + pwr = 2; + else if (p->frequency > 1600000) + pwr = 1; + else if (p->frequency > 1200000) + pwr = 0; + else if (p->frequency >= 1100000) + pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = { + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = p->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + +static void frontend_init(struct budget_patch* budget) +{ + switch(budget->dev->pci->subsystem_device) { + case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X + case 0x1013: // SATELCO Multimedia PCI + + // try the ALPS BSRV2 first of all + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + + // Try the grundig 29504-451 + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { + printk("budget-av: Frontend registration failed!\n"); + dvb_frontend_detach(budget->dvb_frontend); + budget->dvb_frontend = NULL; + } + } +} + +/* written by Emard */ +static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +{ + struct budget_patch *budget; + int err; + int count = 0; + int detected = 0; + +#define PATCH_RESET 0 +#define RPS_IRQ 0 +#define HPS_SETUP 0 +#if PATCH_RESET + saa7146_write(dev, MC1, MASK_31); + msleep(40); +#endif +#if HPS_SETUP + // initialize registers. Better to have it like this + // than leaving something unconfigured + saa7146_write(dev, DD1_STREAM_B, 0); + // port B VSYNC at rising edge + saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! + saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI + + // debi config + // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); + + // zero all HPS registers + saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 + saa7146_write(dev, HPS_H_SCALE, 0); // r6c + saa7146_write(dev, BCS_CTRL, 0); // r70 + saa7146_write(dev, HPS_V_SCALE, 0); // r60 + saa7146_write(dev, HPS_V_GAIN, 0); // r64 + saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 + saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 + // Set HPS prescaler for port B input + saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); + saa7146_write(dev, MC2, + 0 * (MASK_08 | MASK_24) | // BRS control + 0 * (MASK_09 | MASK_25) | // a + 0 * (MASK_10 | MASK_26) | // b + 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 + 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 + 0 * (MASK_01 | MASK_15) // DEBI + ); +#endif + // Disable RPS1 and RPS0 + saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); + // RPS1 timeout disable + saa7146_write(dev, RPS_TOV1, 0); + + // code for autodetection + // will wait for VBI_B event (vertical blank at port B) + // and will reset GPIO3 after VBI_B is detected. + // (GPIO3 should be raised high by CPU to + // test if GPIO3 will generate vertical blank signal + // in budget patch GPIO3 is connected to VSYNC_B + count = 0; +#if 0 + WRITE_RPS1(CMD_UPLOAD | + MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); +#endif + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + // issue RPS1 interrupt to increment counter + WRITE_RPS1(CMD_INTERRUPT); + // at least a NOP is neede between two interrupts + WRITE_RPS1(CMD_NOP); + // interrupt again + WRITE_RPS1(CMD_INTERRUPT); +#endif + WRITE_RPS1(CMD_STOP); + +#if RPS_IRQ + // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) + // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled + // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called + saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + // set event counter 1 threshold to maximum allowed value (rEC p55) + saa7146_write(dev, ECT1R, 0x3fff ); +#endif + // Fix VSYNC level + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + // Set RPS1 Address register to point to RPS code (r108 p42) + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + // Enable RPS1, (rFC p33) + saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); + + + mdelay(50); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + mdelay(150); + + + if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) + detected = 1; + +#if RPS_IRQ + printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +#endif + // Disable RPS1 + saa7146_write(dev, MC1, ( MASK_29 )); + + if(detected == 0) + printk("budget-patch not detected or saa7146 in non-default state.\n" + "try enabling resetting of 7146 with MASK_31 in MC1 register\n"); + + else + printk("BUDGET-PATCH DETECTED.\n"); + + +/* OLD (Original design by Roberto Deza): +** This code will setup the SAA7146_RPS1 to generate a square +** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of +** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; +** then, this GPIO3 output which is connected to the D1B_VSYNC +** input, will trigger the acquisition of the alternate field +** and so on. +** Currently, the TT_budget / WinTV_Nova cards have two ICs +** (74HCT4040, LVC74) for the generation of this VSYNC signal, +** which seems that can be done perfectly without this :-)). +*/ + +/* New design (By Emard) +** this rps1 code will copy internal HS event to GPIO3 pin. +** GPIO3 is in budget-patch hardware connected to port B VSYNC + +** HS is an internal event of 7146, accessible with RPS +** and temporarily raised high every n lines +** (n in defined in the RPS_THRESH1 counter threshold) +** I think HS is raised high on the beginning of the n-th line +** and remains high until this n-th line that triggered +** it is completely received. When the reception of n-th line +** ends, HS is lowered. + +** To transmit data over DMA, 7146 needs changing state at +** port B VSYNC pin. Any changing of port B VSYNC will +** cause some DMA data transfer, with more or less packets loss. +** It depends on the phase and frequency of VSYNC and +** the way of 7146 is instructed to trigger on port B (defined +** in DD1_INIT register, 3rd nibble from the right valid +** numbers are 0-7, see datasheet) +** +** The correct triggering can minimize packet loss, +** dvbtraffic should give this stable bandwidths: +** 22k transponder = 33814 kbit/s +** 27.5k transponder = 38045 kbit/s +** by experiment it is found that the best results +** (stable bandwidths and almost no packet loss) +** are obtained using DD1_INIT triggering number 2 +** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) +** and a VSYNC phase that occurs in the middle of DMA transfer +** (about byte 188*512=96256 in the DMA window). +** +** Phase of HS is still not clear to me how to control, +** It just happens to be so. It can be seen if one enables +** RPS_IRQ and print Event Counter 1 in vpeirq(). Every +** time RPS_INTERRUPT is called, the Event Counter 1 will +** increment. That's how the 7146 is programmed to do event +** counting in this budget-patch.c +** I *think* HPS setting has something to do with the phase +** of HS but I can't be 100% sure in that. + +** hardware debug note: a working budget card (including budget patch) +** with vpeirq() interrupt setup in mode "0x90" (every 64K) will +** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes +** and that means 3*25=75 Hz of interrupt frequency, as seen by +** watch cat /proc/interrupts +** +** If this frequency is 3x lower (and data received in the DMA +** buffer don't start with 0x47, but in the middle of packets, +** whose lengths appear to be like 188 292 188 104 etc. +** this means VSYNC line is not connected in the hardware. +** (check soldering pcb and pins) +** The same behaviour of missing VSYNC can be duplicated on budget +** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. +*/ + + // Setup RPS1 "program" (p35) + count = 0; + + + // Wait Source Line Counter Threshold (p36) + WRITE_RPS1(CMD_PAUSE | EVT_HS); + // Set GPIO3=1 (p42) + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +#if RPS_IRQ + // issue RPS1 interrupt + WRITE_RPS1(CMD_INTERRUPT); +#endif + // Wait reset Source Line Counter Threshold (p36) + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); + // Set GPIO3=0 (p42) + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +#if RPS_IRQ + // issue RPS1 interrupt + WRITE_RPS1(CMD_INTERRUPT); +#endif + // Jump to begin of RPS program (p37) + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); + + // Fix VSYNC level + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + // Set RPS1 Address register to point to RPS code (r108 p42) + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + + if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) + return -ENOMEM; + + dprintk(2, "budget: %p\n", budget); + + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + kfree(budget); + return err; + } + + // Set Source Line Counter Threshold, using BRS (rCC p43) + // It generates HS event every TS_HEIGHT lines + // this is related to TS_WIDTH set in register + // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE + // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 + //,then RPS_THRESH1 + // should be set to trigger every TS_HEIGHT (512) lines. + // + saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); + + // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); + // Enable RPS1 (rFC p33) + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + + dev->ext_priv = budget; + + budget->dvb_adapter.priv = budget; + frontend_init(budget); + + ttpci_budget_init_hooks(budget); + + return 0; +} + +static int budget_patch_detach (struct saa7146_dev* dev) +{ + struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; + int err; + + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } + err = ttpci_budget_deinit (budget); + + kfree (budget); + + return err; +} + +static int __init budget_patch_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_patch_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +static struct saa7146_extension budget_extension = { + .name = "budget_patch dvb", + .flags = 0, + + .module = THIS_MODULE, + .pci_tbl = pci_tbl, + .attach = budget_patch_attach, + .detach = budget_patch_detach, + + .irq_mask = MASK_10, + .irq_func = ttpci_budget_irq10_handler, +}; + +module_init(budget_patch_init); +module_exit(budget_patch_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); +MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 based so-called Budget Patch cards"); diff --git a/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c new file mode 100644 index 000000000000..8c2eca5dcdc9 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "dvb_filter.h" + +static u32 freq[4] = {480, 441, 320, 0}; + +static unsigned int ac3_bitrates[32] = + {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, + 0,0,0,0,0,0,0,0,0,0,0,0,0}; + +static u32 ac3_frames[3][32] = + {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, + 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, + 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, + 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; + +int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) +{ + u8 *headr; + int found = 0; + int c = 0; + u8 frame = 0; + int fr = 0; + + while ( !found && c < count){ + u8 *b = mbuf+c; + + if ( b[0] == 0x0b && b[1] == 0x77 ) + found = 1; + else { + c++; + } + } + + if (!found) return -1; + if (pr) + printk(KERN_DEBUG "Audiostream: AC3"); + + ai->off = c; + if (c+5 >= count) return -1; + + ai->layer = 0; // 0 for AC3 + headr = mbuf+c+2; + + frame = (headr[2]&0x3f); + ai->bit_rate = ac3_bitrates[frame >> 1]*1000; + + if (pr) + printk(KERN_CONT " BRate: %d kb/s", (int) ai->bit_rate/1000); + + ai->frequency = (headr[2] & 0xc0 ) >> 6; + fr = (headr[2] & 0xc0 ) >> 6; + ai->frequency = freq[fr]*100; + if (pr) + printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); + + ai->framesize = ac3_frames[fr][frame >> 1]; + if ((frame & 1) && (fr == 1)) ai->framesize++; + ai->framesize = ai->framesize << 1; + if (pr) + printk(KERN_DEBUG " Framesize %d\n", (int) ai->framesize); + + return 0; +} + +void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, + dvb_filter_pes2ts_cb_t *cb, void *priv) +{ + unsigned char *buf=p2ts->buf; + + buf[0]=0x47; + buf[1]=(pid>>8); + buf[2]=pid&0xff; + p2ts->cc=0; + p2ts->cb=cb; + p2ts->priv=priv; +} + +int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, + int len, int payload_start) +{ + unsigned char *buf=p2ts->buf; + int ret=0, rest; + + //len=6+((pes[4]<<8)|pes[5]); + + if (payload_start) + buf[1]|=0x40; + else + buf[1]&=~0x40; + while (len>=184) { + buf[3]=0x10|((p2ts->cc++)&0x0f); + memcpy(buf+4, pes, 184); + if ((ret=p2ts->cb(p2ts->priv, buf))) + return ret; + len-=184; pes+=184; + buf[1]&=~0x40; + } + if (!len) + return 0; + buf[3]=0x30|((p2ts->cc++)&0x0f); + rest=183-len; + if (rest) { + buf[5]=0x00; + if (rest-1) + memset(buf+6, 0xff, rest-1); + } + buf[4]=rest; + memcpy(buf+5+rest, pes, len); + return p2ts->cb(p2ts->priv, buf); +} diff --git a/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h new file mode 100644 index 000000000000..67a3c6333bca --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h @@ -0,0 +1,242 @@ +/* + * dvb_filter.h + * + * Copyright (C) 2003 Convergence GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DVB_FILTER_H_ +#define _DVB_FILTER_H_ + +#include + +#include + +typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *); + +struct dvb_filter_pes2ts { + unsigned char buf[188]; + unsigned char cc; + dvb_filter_pes2ts_cb_t *cb; + void *priv; +}; + +void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, + dvb_filter_pes2ts_cb_t *cb, void *priv); + +int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, + int len, int payload_start); + + +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define DVB_PICTURE_START 0x00 +#define DVB_USER_START 0xb2 +#define DVB_SEQUENCE_HEADER 0xb3 +#define DVB_SEQUENCE_ERROR 0xb4 +#define DVB_EXTENSION_START 0xb5 +#define DVB_SEQUENCE_END 0xb7 +#define DVB_GOP_START 0xb8 +#define DVB_EXCEPT_SLICE 0xb0 + +#define SEQUENCE_EXTENSION 0x01 +#define SEQUENCE_DISPLAY_EXTENSION 0x02 +#define PICTURE_CODING_EXTENSION 0x08 +#define QUANT_MATRIX_EXTENSION 0x03 +#define PICTURE_DISPLAY_EXTENSION 0x07 + +#define I_FRAME 0x01 +#define B_FRAME 0x02 +#define P_FRAME 0x03 + +/* Initialize sequence_data */ +#define INIT_HORIZONTAL_SIZE 720 +#define INIT_VERTICAL_SIZE 576 +#define INIT_ASPECT_RATIO 0x02 +#define INIT_FRAME_RATE 0x03 +#define INIT_DISP_HORIZONTAL_SIZE 540 +#define INIT_DISP_VERTICAL_SIZE 576 + + +//flags2 +#define PTS_DTS_FLAGS 0xC0 +#define ESCR_FLAG 0x20 +#define ES_RATE_FLAG 0x10 +#define DSM_TRICK_FLAG 0x08 +#define ADD_CPY_FLAG 0x04 +#define PES_CRC_FLAG 0x02 +#define PES_EXT_FLAG 0x01 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 + +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +#define MAX_PLENGTH 0xFFFF +#define MMAX_PLENGTH (256*MAX_PLENGTH) + +#ifndef IPACKS +#define IPACKS 2048 +#endif + +struct ipack { + int size; + int found; + u8 *buf; + u8 cid; + u32 plength; + u8 plen[2]; + u8 flag1; + u8 flag2; + u8 hlength; + u8 pts[5]; + u16 *pid; + int mpeg; + u8 check; + int which; + int done; + void *data; + void (*func)(u8 *buf, int size, void *priv); + int count; + int repack_subids; +}; + +struct dvb_video_info { + u32 horizontal_size; + u32 vertical_size; + u32 aspect_ratio; + u32 framerate; + u32 video_format; + u32 bit_rate; + u32 comp_bit_rate; + u32 vbv_buffer_size; + s16 vbv_delay; + u32 CSPF; + u32 off; +}; + +#define OFF_SIZE 4 +#define FIRST_FIELD 0 +#define SECOND_FIELD 1 +#define VIDEO_FRAME_PICTURE 0x03 + +struct mpg_picture { + int channel; + struct dvb_video_info vinfo; + u32 *sequence_gop_header; + u32 *picture_header; + s32 time_code; + int low_delay; + int closed_gop; + int broken_link; + int sequence_header_flag; + int gop_flag; + int sequence_end_flag; + + u8 profile_and_level; + s32 picture_coding_parameter; + u32 matrix[32]; + s8 matrix_change_flag; + + u8 picture_header_parameter; + /* bit 0 - 2: bwd f code + bit 3 : fpb vector + bit 4 - 6: fwd f code + bit 7 : fpf vector */ + + int mpeg1_flag; + int progressive_sequence; + int sequence_display_extension_flag; + u32 sequence_header_data; + s16 last_frame_centre_horizontal_offset; + s16 last_frame_centre_vertical_offset; + + u32 pts[2]; /* [0] 1st field, [1] 2nd field */ + int top_field_first; + int repeat_first_field; + int progressive_frame; + int bank; + int forward_bank; + int backward_bank; + int compress; + s16 frame_centre_horizontal_offset[OFF_SIZE]; + /* [0-2] 1st field, [3] 2nd field */ + s16 frame_centre_vertical_offset[OFF_SIZE]; + /* [0-2] 1st field, [3] 2nd field */ + s16 temporal_reference[2]; + /* [0] 1st field, [1] 2nd field */ + + s8 picture_coding_type[2]; + /* [0] 1st field, [1] 2nd field */ + s8 picture_structure[2]; + /* [0] 1st field, [1] 2nd field */ + s8 picture_display_extension_flag[2]; + /* [0] 1st field, [1] 2nd field */ + /* picture_display_extenion() 0:no 1:exit*/ + s8 pts_flag[2]; + /* [0] 1st field, [1] 2nd field */ +}; + +struct dvb_audio_info { + int layer; + u32 bit_rate; + u32 frequency; + u32 mode; + u32 mode_extension ; + u32 emphasis; + u32 framesize; + u32 off; +}; + +int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr); + + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/av7110/sp8870.c b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.c new file mode 100644 index 000000000000..9767159aeb9b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + +*/ +/* + * This driver needs external firmware. Please use the command + * "/scripts/get_dvb_firmware alps_tdlb7" to + * download/extract it, and then copy it to /usr/lib/hotplug/firmware + * or /lib/firmware (depending on configuration of firmware hotplug). + */ +#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sp8870.h" + + +struct sp8870_state { + + struct i2c_adapter* i2c; + + const struct sp8870_config* config; + + struct dvb_frontend frontend; + + /* demodulator private data */ + u8 initialised:1; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "sp8870: " args); \ + } while (0) + +/* firmware size for sp8870 */ +#define SP8870_FIRMWARE_SIZE 16382 + +/* starting point for firmware in file 'Sc_main.mc' */ +#define SP8870_FIRMWARE_OFFSET 0x0A + +static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) +{ + u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; + int err; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int sp8870_readreg (struct sp8870_state* state, u16 reg) +{ + int ret; + u8 b0 [] = { reg >> 8 , reg & 0xff }; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: readreg error (ret == %i)\n", __func__, ret); + return -1; + } + + return (b1[0] << 8 | b1[1]); +} + +static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) +{ + struct i2c_msg msg; + const char *fw_buf = fw->data; + int fw_pos; + u8 tx_buf[255]; + int tx_len; + int err = 0; + + dprintk ("%s: ...\n", __func__); + + if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) + return -EINVAL; + + // system controller stop + sp8870_writereg(state, 0x0F00, 0x0000); + + // instruction RAM register hiword + sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); + + // instruction RAM MWR + sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); + + // do firmware upload + fw_pos = SP8870_FIRMWARE_OFFSET; + while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ + tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; + // write register 0xCF0A + tx_buf[0] = 0xCF; + tx_buf[1] = 0x0A; + memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = tx_buf; + msg.len = tx_len + 2; + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + printk("%s: firmware upload failed!\n", __func__); + printk ("%s: i2c error (err == %i)\n", __func__, err); + return err; + } + fw_pos += tx_len; + } + + dprintk ("%s: done!\n", __func__); + return 0; +}; + +static void sp8870_microcontroller_stop (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller STOP + sp8870_writereg(state, 0x0F00, 0x000); +} + +static void sp8870_microcontroller_start (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller START + sp8870_writereg(state, 0x0F00, 0x001); + // not documented but if we don't read 0x0D01 out here + // we don't get a correct data valid signal + sp8870_readreg(state, 0x0D01); +} + +static int sp8870_read_data_valid_signal(struct sp8870_state* state) +{ + return (sp8870_readreg(state, 0x0D02) > 0); +} + +static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) +{ + int known_parameters = 1; + + *reg0xc05 = 0x000; + + switch (p->modulation) { + case QPSK: + break; + case QAM_16: + *reg0xc05 |= (1 << 10); + break; + case QAM_64: + *reg0xc05 |= (2 << 10); + break; + case QAM_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + switch (p->hierarchy) { + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + *reg0xc05 |= (1 << 7); + break; + case HIERARCHY_2: + *reg0xc05 |= (2 << 7); + break; + case HIERARCHY_4: + *reg0xc05 |= (3 << 7); + break; + case HIERARCHY_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + switch (p->code_rate_HP) { + case FEC_1_2: + break; + case FEC_2_3: + *reg0xc05 |= (1 << 3); + break; + case FEC_3_4: + *reg0xc05 |= (2 << 3); + break; + case FEC_5_6: + *reg0xc05 |= (3 << 3); + break; + case FEC_7_8: + *reg0xc05 |= (4 << 3); + break; + case FEC_AUTO: + known_parameters = 0; + break; + default: + return -EINVAL; + } + + if (known_parameters) + *reg0xc05 |= (2 << 1); /* use specified parameters */ + else + *reg0xc05 |= (1 << 1); /* enable autoprobing */ + + return 0; +} + +static int sp8870_wake_up(struct sp8870_state* state) +{ + // enable TS output and interface pins + return sp8870_writereg(state, 0xC18, 0x00D); +} + +static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct sp8870_state* state = fe->demodulator_priv; + int err; + u16 reg0xc05; + + if ((err = configure_reg0xc05(p, ®0xc05))) + return err; + + // system controller stop + sp8870_microcontroller_stop(state); + + // set tuner parameters + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + // sample rate correction bit [23..17] + sp8870_writereg(state, 0x0319, 0x000A); + + // sample rate correction bit [16..0] + sp8870_writereg(state, 0x031A, 0x0AAB); + + // integer carrier offset + sp8870_writereg(state, 0x0309, 0x0400); + + // fractional carrier offset + sp8870_writereg(state, 0x030A, 0x0000); + + // filter for 6/7/8 Mhz channel + if (p->bandwidth_hz == 6000000) + sp8870_writereg(state, 0x0311, 0x0002); + else if (p->bandwidth_hz == 7000000) + sp8870_writereg(state, 0x0311, 0x0001); + else + sp8870_writereg(state, 0x0311, 0x0000); + + // scan order: 2k first = 0x0000, 8k first = 0x0001 + if (p->transmission_mode == TRANSMISSION_MODE_2K) + sp8870_writereg(state, 0x0338, 0x0000); + else + sp8870_writereg(state, 0x0338, 0x0001); + + sp8870_writereg(state, 0xc05, reg0xc05); + + // read status reg in order to clear pending irqs + err = sp8870_readreg(state, 0x200); + if (err < 0) + return err; + + // system controller start + sp8870_microcontroller_start(state); + + return 0; +} + +static int sp8870_init (struct dvb_frontend* fe) +{ + struct sp8870_state* state = fe->demodulator_priv; + const struct firmware *fw = NULL; + + sp8870_wake_up(state); + if (state->initialised) return 0; + state->initialised = 1; + + dprintk ("%s\n", __func__); + + + /* request the firmware, this will block until someone uploads it */ + printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); + if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { + printk("sp8870: no firmware upload (timeout or file not found?)\n"); + return -EIO; + } + + if (sp8870_firmware_upload(state, fw)) { + printk("sp8870: writing firmware to device failed\n"); + release_firmware(fw); + return -EIO; + } + release_firmware(fw); + printk("sp8870: firmware upload complete\n"); + + /* enable TS output and interface pins */ + sp8870_writereg(state, 0xc18, 0x00d); + + // system controller stop + sp8870_microcontroller_stop(state); + + // ADC mode + sp8870_writereg(state, 0x0301, 0x0003); + + // Reed Solomon parity bytes passed to output + sp8870_writereg(state, 0x0C13, 0x0001); + + // MPEG clock is suppressed if no valid data + sp8870_writereg(state, 0x0C14, 0x0001); + + /* bit 0x010: enable data valid signal */ + sp8870_writereg(state, 0x0D00, 0x010); + sp8870_writereg(state, 0x0D01, 0x000); + + return 0; +} + +static int sp8870_read_status(struct dvb_frontend *fe, + enum fe_status *fe_status) +{ + struct sp8870_state* state = fe->demodulator_priv; + int status; + int signal; + + *fe_status = 0; + + status = sp8870_readreg (state, 0x0200); + if (status < 0) + return -EIO; + + signal = sp8870_readreg (state, 0x0303); + if (signal < 0) + return -EIO; + + if (signal > 0x0F) + *fe_status |= FE_HAS_SIGNAL; + if (status & 0x08) + *fe_status |= FE_HAS_SYNC; + if (status & 0x04) + *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; + + return 0; +} + +static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) +{ + struct sp8870_state* state = fe->demodulator_priv; + int ret; + u32 tmp; + + *ber = 0; + + ret = sp8870_readreg(state, 0xC08); + if (ret < 0) + return -EIO; + + tmp = ret & 0x3F; + + ret = sp8870_readreg(state, 0xC07); + if (ret < 0) + return -EIO; + + tmp = ret << 6; + if (tmp >= 0x3FFF0) + tmp = ~0; + + *ber = tmp; + + return 0; +} + +static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct sp8870_state* state = fe->demodulator_priv; + int ret; + u16 tmp; + + *signal = 0; + + ret = sp8870_readreg (state, 0x306); + if (ret < 0) + return -EIO; + + tmp = ret << 8; + + ret = sp8870_readreg (state, 0x303); + if (ret < 0) + return -EIO; + + tmp |= ret; + + if (tmp) + *signal = 0xFFFF - tmp; + + return 0; +} + +static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) +{ + struct sp8870_state* state = fe->demodulator_priv; + int ret; + + *ublocks = 0; + + ret = sp8870_readreg(state, 0xC0C); + if (ret < 0) + return -EIO; + + if (ret == 0xFFFF) + ret = ~0; + + *ublocks = ret; + + return 0; +} + +/* number of trials to recover from lockup */ +#define MAXTRIALS 5 +/* maximum checks for data valid signal */ +#define MAXCHECKS 100 + +/* only for debugging: counter for detected lockups */ +static int lockups; +/* only for debugging: counter for channel switches */ +static int switches; + +static int sp8870_set_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct sp8870_state* state = fe->demodulator_priv; + + /* + The firmware of the sp8870 sometimes locks up after setting frontend parameters. + We try to detect this by checking the data valid signal. + If it is not set after MAXCHECKS we try to recover the lockup by setting + the frontend parameters again. + */ + + int err = 0; + int valid = 0; + int trials = 0; + int check_count = 0; + + dprintk("%s: frequency = %i\n", __func__, p->frequency); + + for (trials = 1; trials <= MAXTRIALS; trials++) { + + err = sp8870_set_frontend_parameters(fe); + if (err) + return err; + + for (check_count = 0; check_count < MAXCHECKS; check_count++) { +// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); + valid = sp8870_read_data_valid_signal(state); + if (valid) { + dprintk("%s: delay = %i usec\n", + __func__, check_count * 10); + break; + } + udelay(10); + } + if (valid) + break; + } + + if (!valid) { + printk("%s: firmware crash!!!!!!\n", __func__); + return -EIO; + } + + if (debug) { + if (valid) { + if (trials > 1) { + printk("%s: firmware lockup!!!\n", __func__); + printk("%s: recovered after %i trial(s))\n", __func__, trials - 1); + lockups++; + } + } + switches++; + printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups); + } + + return 0; +} + +static int sp8870_sleep(struct dvb_frontend* fe) +{ + struct sp8870_state* state = fe->demodulator_priv; + + // tristate TS output and disable interface pins + return sp8870_writereg(state, 0xC18, 0x000); +} + +static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 350; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct sp8870_state* state = fe->demodulator_priv; + + if (enable) { + return sp8870_writereg(state, 0x206, 0x001); + } else { + return sp8870_writereg(state, 0x206, 0x000); + } +} + +static void sp8870_release(struct dvb_frontend* fe) +{ + struct sp8870_state* state = fe->demodulator_priv; + kfree(state); +} + +static const struct dvb_frontend_ops sp8870_ops; + +struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c) +{ + struct sp8870_state* state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + + /* check if the demod is there */ + if (sp8870_readreg(state, 0x0200) < 0) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static const struct dvb_frontend_ops sp8870_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Spase SP8870 DVB-T", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER + }, + + .release = sp8870_release, + + .init = sp8870_init, + .sleep = sp8870_sleep, + .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, + + .set_frontend = sp8870_set_frontend, + .get_tune_settings = sp8870_get_tune_settings, + + .read_status = sp8870_read_status, + .read_ber = sp8870_read_ber, + .read_signal_strength = sp8870_read_signal_strength, + .read_ucblocks = sp8870_read_uncorrected_blocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); +MODULE_AUTHOR("Juergen Peitz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(sp8870_attach); diff --git a/drivers/staging/media/deprecated/saa7146/av7110/sp8870.h b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.h new file mode 100644 index 000000000000..5eacf39f425e --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + +*/ + +#ifndef SP8870_H +#define SP8870_H + +#include +#include + +struct sp8870_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +#if IS_REACHABLE(CONFIG_DVB_SP8870) +extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_DVB_SP8870 + +#endif // SP8870_H diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst new file mode 100644 index 000000000000..a7730559bbb2 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_CLEAR_BUFFER: + +================== +VIDEO_CLEAR_BUFFER +================== + +Name +---- + +VIDEO_CLEAR_BUFFER + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_CLEAR_BUFFER + +``int ioctl(fd, VIDEO_CLEAR_BUFFER)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_CLEAR_BUFFER for this command. + +Description +----------- + +This ioctl call clears all video buffers in the driver and in the +decoder hardware. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-command.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-command.rst new file mode 100644 index 000000000000..cae9445eb3af --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-command.rst @@ -0,0 +1,96 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_COMMAND: + +============= +VIDEO_COMMAND +============= + +Name +---- + +VIDEO_COMMAND + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_COMMAND + +``int ioctl(int fd, VIDEO_COMMAND, struct video_command *cmd)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_COMMAND for this command. + + - .. row 3 + + - struct video_command \*cmd + + - Commands the decoder. + +Description +----------- + +This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders +this ioctl has been replaced by the +:ref:`VIDIOC_DECODER_CMD` ioctl. + +This ioctl commands the decoder. The ``video_command`` struct is a +subset of the ``v4l2_decoder_cmd`` struct, so refer to the +:ref:`VIDIOC_DECODER_CMD` documentation for +more information. + +.. c:type:: video_command + +.. code-block:: c + + /* The structure must be zeroed before use by the application + This ensures it can be extended safely in the future. */ + struct video_command { + __u32 cmd; + __u32 flags; + union { + struct { + __u64 pts; + } stop; + + struct { + /* 0 or 1000 specifies normal speed, + 1 specifies forward single stepping, + -1 specifies backward single stepping, + >1: playback at speed/1000 of the normal speed, + <-1: reverse playback at (-speed/1000) of the normal speed. */ + __s32 speed; + __u32 format; + } play; + + struct { + __u32 data[16]; + } raw; + }; + }; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst new file mode 100644 index 000000000000..bc34bf3989e4 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_CONTINUE: + +============== +VIDEO_CONTINUE +============== + +Name +---- + +VIDEO_CONTINUE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_CONTINUE + +``int ioctl(fd, VIDEO_CONTINUE)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_CONTINUE for this command. + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 :ref:`VIDIOC_DECODER_CMD` instead. + +This ioctl call restarts decoding and playing processes of the video +stream which was played before a call to VIDEO_FREEZE was made. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst new file mode 100644 index 000000000000..e71fa8d6965b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_FAST_FORWARD: + +================== +VIDEO_FAST_FORWARD +================== + +Name +---- + +VIDEO_FAST_FORWARD + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_FAST_FORWARD + +``int ioctl(fd, VIDEO_FAST_FORWARD, int nFrames)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_FAST_FORWARD for this command. + + - .. row 3 + + - int nFrames + + - The number of frames to skip. + +Description +----------- + +This ioctl call asks the Video Device to skip decoding of N number of +I-frames. This call can only be used if VIDEO_SOURCE_MEMORY is +selected. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EPERM`` + + - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst new file mode 100644 index 000000000000..01d24d548439 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _video_fclose: + +================= +dvb video close() +================= + +Name +---- + +dvb video close() + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:function:: int close(int fd) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + +Description +----------- + +This system call closes a previously opened video device. + +Return Value +------------ + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EBADF`` + + - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst new file mode 100644 index 000000000000..1371b083e4e8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst @@ -0,0 +1,111 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _video_fopen: + +================ +dvb video open() +================ + +Name +---- + +dvb video open() + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:function:: int open(const char *deviceName, int flags) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - const char \*deviceName + + - Name of specific video device. + + - .. row 2 + + - int flags + + - A bit-wise OR of the following flags: + + - .. row 3 + + - + - O_RDONLY read-only access + + - .. row 4 + + - + - O_RDWR read/write access + + - .. row 5 + + - + - O_NONBLOCK open in non-blocking mode + + - .. row 6 + + - + - (blocking mode is the default) + +Description +----------- + +This system call opens a named video device (e.g. +/dev/dvb/adapter0/video0) for subsequent use. + +When an open() call has succeeded, the device will be ready for use. The +significance of blocking or non-blocking mode is described in the +documentation for functions where there is a difference. It does not +affect the semantics of the open() call itself. A device opened in +blocking mode can later be put into non-blocking mode (and vice versa) +using the F_SETFL command of the fcntl system call. This is a standard +system call, documented in the Linux manual page for fcntl. Only one +user can open the Video Device in O_RDWR mode. All other attempts to +open the device in this mode will fail, and an error-code will be +returned. If the Video Device is opened in O_RDONLY mode, the only +ioctl call that can be used is VIDEO_GET_STATUS. All other call will +return an error code. + +Return Value +------------ + +.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``ENODEV`` + + - Device driver not loaded/available. + + - .. row 2 + + - ``EINTERNAL`` + + - Internal error. + + - .. row 3 + + - ``EBUSY`` + + - Device or resource busy. + + - .. row 4 + + - ``EINVAL`` + + - Invalid argument. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst new file mode 100644 index 000000000000..4321f257cb70 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_FREEZE: + +============ +VIDEO_FREEZE +============ + +Name +---- + +VIDEO_FREEZE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_FREEZE + +``int ioctl(fd, VIDEO_FREEZE)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_FREEZE for this command. + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 :ref:`VIDIOC_DECODER_CMD` instead. + +This ioctl call suspends the live video stream being played. Decoding +and playing are frozen. It is then possible to restart the decoding and +playing process of the video stream using the VIDEO_CONTINUE command. +If VIDEO_SOURCE_MEMORY is selected in the ioctl call +VIDEO_SELECT_SOURCE, the Digital TV subsystem will not decode any more data +until the ioctl call VIDEO_CONTINUE or VIDEO_PLAY is performed. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst new file mode 100644 index 000000000000..a07fd7d7a40e --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst @@ -0,0 +1,79 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _video_fwrite: + +================= +dvb video write() +================= + +Name +---- + +dvb video write() + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:function:: size_t write(int fd, const void *buf, size_t count) + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - void \*buf + + - Pointer to the buffer containing the PES data. + + - .. row 3 + + - size_t count + + - Size of buf. + +Description +----------- + +This system call can only be used if VIDEO_SOURCE_MEMORY is selected +in the ioctl call VIDEO_SELECT_SOURCE. The data provided shall be in +PES format, unless the capability allows other formats. If O_NONBLOCK +is not specified the function will block until buffer space is +available. The amount of data to be transferred is implied by count. + +Return Value +------------ + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EPERM`` + + - Mode VIDEO_SOURCE_MEMORY not selected. + + - .. row 2 + + - ``ENOMEM`` + + - Attempted to write more data than the internal buffer can hold. + + - .. row 3 + + - ``EBADF`` + + - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst new file mode 100644 index 000000000000..01e09f56656c --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_CAPABILITIES: + +====================== +VIDEO_GET_CAPABILITIES +====================== + +Name +---- + +VIDEO_GET_CAPABILITIES + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_CAPABILITIES + +``int ioctl(fd, VIDEO_GET_CAPABILITIES, unsigned int *cap)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_CAPABILITIES for this command. + + - .. row 3 + + - unsigned int \*cap + + - Pointer to a location where to store the capability information. + +Description +----------- + +This ioctl call asks the video device about its decoding capabilities. +On success it returns and integer which has bits set according to the +defines in section ??. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst new file mode 100644 index 000000000000..90382bc36cfe --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_EVENT: + +=============== +VIDEO_GET_EVENT +=============== + +Name +---- + +VIDEO_GET_EVENT + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_EVENT + +``int ioctl(fd, VIDEO_GET_EVENT, struct video_event *ev)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_EVENT for this command. + + - .. row 3 + + - struct video_event \*ev + + - Points to the location where the event, if any, is to be stored. + +Description +----------- + +This ioctl is for Digital TV devices only. To get events from a V4L2 decoder +use the V4L2 :ref:`VIDIOC_DQEVENT` ioctl instead. + +This ioctl call returns an event of type video_event if available. If +an event is not available, the behavior depends on whether the device is +in blocking or non-blocking mode. In the latter case, the call fails +immediately with errno set to ``EWOULDBLOCK``. In the former case, the call +blocks until an event becomes available. The standard Linux poll() +and/or select() system calls can be used with the device file descriptor +to watch for new events. For select(), the file descriptor should be +included in the exceptfds argument, and for poll(), POLLPRI should be +specified as the wake-up condition. Read-only permissions are sufficient +for this ioctl call. + +.. c:type:: video_event + +.. code-block:: c + + struct video_event { + __s32 type; + #define VIDEO_EVENT_SIZE_CHANGED 1 + #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 + #define VIDEO_EVENT_DECODER_STOPPED 3 + #define VIDEO_EVENT_VSYNC 4 + long timestamp; + union { + video_size_t size; + unsigned int frame_rate; /* in frames per 1000sec */ + unsigned char vsync_field; /* unknown/odd/even/progressive */ + } u; + }; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EWOULDBLOCK`` + + - There is no event pending, and the device is in non-blocking mode. + + - .. row 2 + + - ``EOVERFLOW`` + + - Overflow in event queue - one or more events were lost. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst new file mode 100644 index 000000000000..b48ac8c58a41 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_FRAME_COUNT: + +===================== +VIDEO_GET_FRAME_COUNT +===================== + +Name +---- + +VIDEO_GET_FRAME_COUNT + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_FRAME_COUNT + +``int ioctl(int fd, VIDEO_GET_FRAME_COUNT, __u64 *pts)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_FRAME_COUNT for this command. + + - .. row 3 + + - __u64 \*pts + + - Returns the number of frames displayed since the decoder was + started. + +Description +----------- + +This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders +this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_FRAME`` +control. + +This ioctl call asks the Video Device to return the number of displayed +frames since the decoder was started. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst new file mode 100644 index 000000000000..fedaff41be0b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst @@ -0,0 +1,69 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_PTS: + +============= +VIDEO_GET_PTS +============= + +Name +---- + +VIDEO_GET_PTS + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_PTS + +``int ioctl(int fd, VIDEO_GET_PTS, __u64 *pts)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_PTS for this command. + + - .. row 3 + + - __u64 \*pts + + - Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / + ISO/IEC 13818-1. + + The PTS should belong to the currently played frame if possible, + but may also be a value close to it like the PTS of the last + decoded frame or the last PTS extracted by the PES parser. + +Description +----------- + +This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders +this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_PTS`` +control. + +This ioctl call asks the Video Device to return the current PTS +timestamp. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst new file mode 100644 index 000000000000..de34331c5bd1 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst @@ -0,0 +1,69 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_SIZE: + +============== +VIDEO_GET_SIZE +============== + +Name +---- + +VIDEO_GET_SIZE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_SIZE + +``int ioctl(int fd, VIDEO_GET_SIZE, video_size_t *size)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_SIZE for this command. + + - .. row 3 + + - video_size_t \*size + + - Returns the size and aspect ratio. + +Description +----------- + +This ioctl returns the size and aspect ratio. + +.. c:type:: video_size_t + +.. code-block::c + + typedef struct { + int w; + int h; + video_format_t aspect_ratio; + } video_size_t; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst new file mode 100644 index 000000000000..9b86fbf411d4 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_GET_STATUS: + +================ +VIDEO_GET_STATUS +================ + +Name +---- + +VIDEO_GET_STATUS + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_GET_STATUS + +``int ioctl(fd, VIDEO_GET_STATUS, struct video_status *status)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_GET_STATUS for this command. + + - .. row 3 + + - struct video_status \*status + + - Returns the current status of the Video Device. + +Description +----------- + +This ioctl call asks the Video Device to return the current status of +the device. + +.. c:type:: video_status + +.. code-block:: c + + struct video_status { + int video_blank; /* blank video on freeze? */ + video_play_state_t play_state; /* current state of playback */ + video_stream_source_t stream_source; /* current source (demux/memory) */ + video_format_t video_format; /* current aspect ratio of stream*/ + video_displayformat_t display_format;/* selected cropping mode */ + }; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-play.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-play.rst new file mode 100644 index 000000000000..35ac8b98fdbf --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-play.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_PLAY: + +========== +VIDEO_PLAY +========== + +Name +---- + +VIDEO_PLAY + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_PLAY + +``int ioctl(fd, VIDEO_PLAY)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_PLAY for this command. + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 :ref:`VIDIOC_DECODER_CMD` instead. + +This ioctl call asks the Video Device to start playing a video stream +from the selected source. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst new file mode 100644 index 000000000000..929a20985d53 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst @@ -0,0 +1,76 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SELECT_SOURCE: + +=================== +VIDEO_SELECT_SOURCE +=================== + +Name +---- + +VIDEO_SELECT_SOURCE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SELECT_SOURCE + +``int ioctl(fd, VIDEO_SELECT_SOURCE, video_stream_source_t source)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SELECT_SOURCE for this command. + + - .. row 3 + + - video_stream_source_t source + + - Indicates which source shall be used for the Video stream. + +Description +----------- + +This ioctl is for Digital TV devices only. This ioctl was also supported by the +V4L2 ivtv driver, but that has been replaced by the ivtv-specific +``IVTV_IOC_PASSTHROUGH_MODE`` ioctl. + +This ioctl call informs the video device which source shall be used for +the input data. The possible sources are demux or memory. If memory is +selected, the data is fed to the video device through the write command. + +.. c:type:: video_stream_source_t + +.. code-block:: c + + typedef enum { + VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ + VIDEO_SOURCE_MEMORY /* If this source is selected, the stream + comes from the user through the write + system call */ + } video_stream_source_t; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst new file mode 100644 index 000000000000..70249a6ba125 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst @@ -0,0 +1,64 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SET_BLANK: + +=============== +VIDEO_SET_BLANK +=============== + +Name +---- + +VIDEO_SET_BLANK + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SET_BLANK + +``int ioctl(fd, VIDEO_SET_BLANK, boolean mode)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SET_BLANK for this command. + + - .. row 3 + + - boolean mode + + - TRUE: Blank screen when stop. + + - .. row 4 + + - + - FALSE: Show last decoded frame. + +Description +----------- + +This ioctl call asks the Video Device to blank out the picture. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst new file mode 100644 index 000000000000..1de4f40ae732 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SET_DISPLAY_FORMAT: + +======================== +VIDEO_SET_DISPLAY_FORMAT +======================== + +Name +---- + +VIDEO_SET_DISPLAY_FORMAT + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SET_DISPLAY_FORMAT + +``int ioctl(fd, VIDEO_SET_DISPLAY_FORMAT)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SET_DISPLAY_FORMAT for this command. + + - .. row 3 + + - video_display_format_t format + + - Selects the video format to be used. + +Description +----------- + +This ioctl call asks the Video Device to select the video format to be +applied by the MPEG chip on the video. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst new file mode 100644 index 000000000000..bb64e37ae081 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst @@ -0,0 +1,82 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SET_FORMAT: + +================ +VIDEO_SET_FORMAT +================ + +Name +---- + +VIDEO_SET_FORMAT + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SET_FORMAT + +``int ioctl(fd, VIDEO_SET_FORMAT, video_format_t format)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SET_FORMAT for this command. + + - .. row 3 + + - video_format_t format + + - video format of TV as defined in section ??. + +Description +----------- + +This ioctl sets the screen format (aspect ratio) of the connected output +device (TV) so that the output of the decoder can be adjusted +accordingly. + +.. c:type:: video_format_t + +.. code-block:: c + + typedef enum { + VIDEO_FORMAT_4_3, /* Select 4:3 format */ + VIDEO_FORMAT_16_9, /* Select 16:9 format. */ + VIDEO_FORMAT_221_1 /* 2.21:1 */ + } video_format_t; + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EINVAL`` + + - format is not a valid video format. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst new file mode 100644 index 000000000000..1f31c048bdbc --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SET_STREAMTYPE: + +==================== +VIDEO_SET_STREAMTYPE +==================== + +Name +---- + +VIDEO_SET_STREAMTYPE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SET_STREAMTYPE + +``int ioctl(fd, VIDEO_SET_STREAMTYPE, int type)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SET_STREAMTYPE for this command. + + - .. row 3 + + - int type + + - stream type + +Description +----------- + +This ioctl tells the driver which kind of stream to expect being written +to it. If this call is not used the default of video PES is used. Some +drivers might not support this call and always expect PES. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst new file mode 100644 index 000000000000..1478fcc30cb8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_SLOWMOTION: + +================ +VIDEO_SLOWMOTION +================ + +Name +---- + +VIDEO_SLOWMOTION + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_SLOWMOTION + +``int ioctl(fd, VIDEO_SLOWMOTION, int nFrames)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_SLOWMOTION for this command. + + - .. row 3 + + - int nFrames + + - The number of times to repeat each frame. + +Description +----------- + +This ioctl call asks the video device to repeat decoding frames N number +of times. This call can only be used if VIDEO_SOURCE_MEMORY is +selected. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - ``EPERM`` + + - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst new file mode 100644 index 000000000000..d25384222a20 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_STILLPICTURE: + +================== +VIDEO_STILLPICTURE +================== + +Name +---- + +VIDEO_STILLPICTURE + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_STILLPICTURE + +``int ioctl(fd, VIDEO_STILLPICTURE, struct video_still_picture *sp)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_STILLPICTURE for this command. + + - .. row 3 + + - struct video_still_picture \*sp + + - Pointer to a location where an I-frame and size is stored. + +Description +----------- + +This ioctl call asks the Video Device to display a still picture +(I-frame). The input data shall contain an I-frame. If the pointer is +NULL, then the current displayed still picture is blanked. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst new file mode 100644 index 000000000000..96f61c5b48a2 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_STOP: + +========== +VIDEO_STOP +========== + +Name +---- + +VIDEO_STOP + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_STOP + +``int ioctl(fd, VIDEO_STOP, boolean mode)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_STOP for this command. + + - .. row 3 + + - Boolean mode + + - Indicates how the screen shall be handled. + + - .. row 4 + + - + - TRUE: Blank screen when stop. + + - .. row 5 + + - + - FALSE: Show last decoded frame. + +Description +----------- + +This ioctl is for Digital TV devices only. To control a V4L2 decoder use the +V4L2 :ref:`VIDIOC_DECODER_CMD` instead. + +This ioctl call asks the Video Device to stop playing the current +stream. Depending on the input parameter, the screen can be blanked out +or displaying the last decoded frame. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst new file mode 100644 index 000000000000..79bf3dfb8a32 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst @@ -0,0 +1,66 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. c:namespace:: DTV.video + +.. _VIDEO_TRY_COMMAND: + +================= +VIDEO_TRY_COMMAND +================= + +Name +---- + +VIDEO_TRY_COMMAND + +.. attention:: This ioctl is deprecated. + +Synopsis +-------- + +.. c:macro:: VIDEO_TRY_COMMAND + +``int ioctl(int fd, VIDEO_TRY_COMMAND, struct video_command *cmd)`` + +Arguments +--------- + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + - .. row 1 + + - int fd + + - File descriptor returned by a previous call to open(). + + - .. row 2 + + - int request + + - Equals VIDEO_TRY_COMMAND for this command. + + - .. row 3 + + - struct video_command \*cmd + + - Try a decoder command. + +Description +----------- + +This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders +this ioctl has been replaced by the +:ref:`VIDIOC_TRY_DECODER_CMD ` ioctl. + +This ioctl tries a decoder command. The ``video_command`` struct is a +subset of the ``v4l2_decoder_cmd`` struct, so refer to the +:ref:`VIDIOC_TRY_DECODER_CMD ` documentation +for more information. + +Return Value +------------ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video.rst b/drivers/staging/media/deprecated/saa7146/av7110/video.rst new file mode 100644 index 000000000000..808705b769a1 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _dvb_video: + +####################### +Digital TV Video Device +####################### + +The Digital TV video device controls the MPEG2 video decoder of the Digital +TV hardware. It can be accessed through **/dev/dvb/adapter0/video0**. Data +types and ioctl definitions can be accessed by including +**linux/dvb/video.h** in your application. + +Note that the Digital TV video device only controls decoding of the MPEG video +stream, not its presentation on the TV or computer screen. On PCs this +is typically handled by an associated video4linux device, e.g. +**/dev/video**, which allows scaling and defining output windows. + +Some Digital TV cards don't have their own MPEG decoder, which results in the +omission of the audio and video device as well as the video4linux +device. + +The ioctls that deal with SPUs (sub picture units) and navigation +packets are only supported on some MPEG decoders made for DVD playback. + +These ioctls were also used by V4L2 to control MPEG decoders implemented +in V4L2. The use of these ioctls for that purpose has been made obsolete +and proper V4L2 ioctls or controls have been created to replace that +functionality. + + +.. toctree:: + :maxdepth: 1 + + video_types + video_function_calls diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst b/drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst new file mode 100644 index 000000000000..20a897be5dca --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _video_function_calls: + +******************** +Video Function Calls +******************** + +.. toctree:: + :maxdepth: 1 + + video-fopen + video-fclose + video-fwrite + video-stop + video-play + video-freeze + video-continue + video-select-source + video-set-blank + video-get-status + video-get-frame-count + video-get-pts + video-get-event + video-command + video-try-command + video-get-size + video-set-display-format + video-stillpicture + video-fast-forward + video-slowmotion + video-get-capabilities + video-clear-buffer + video-set-streamtype + video-set-format diff --git a/drivers/staging/media/deprecated/saa7146/av7110/video_types.rst b/drivers/staging/media/deprecated/saa7146/av7110/video_types.rst new file mode 100644 index 000000000000..c4557d328b7a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/video_types.rst @@ -0,0 +1,248 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _video_types: + +**************** +Video Data Types +**************** + + +.. _video-format-t: + +video_format_t +============== + +The ``video_format_t`` data type defined by + + +.. code-block:: c + + typedef enum { + VIDEO_FORMAT_4_3, /* Select 4:3 format */ + VIDEO_FORMAT_16_9, /* Select 16:9 format. */ + VIDEO_FORMAT_221_1 /* 2.21:1 */ + } video_format_t; + +is used in the VIDEO_SET_FORMAT function (??) to tell the driver which +aspect ratio the output hardware (e.g. TV) has. It is also used in the +data structures video_status (??) returned by VIDEO_GET_STATUS (??) +and video_event (??) returned by VIDEO_GET_EVENT (??) which report +about the display format of the current video stream. + + +.. _video-displayformat-t: + +video_displayformat_t +===================== + +In case the display format of the video stream and of the display +hardware differ the application has to specify how to handle the +cropping of the picture. This can be done using the +VIDEO_SET_DISPLAY_FORMAT call (??) which accepts + + +.. code-block:: c + + typedef enum { + VIDEO_PAN_SCAN, /* use pan and scan format */ + VIDEO_LETTER_BOX, /* use letterbox format */ + VIDEO_CENTER_CUT_OUT /* use center cut out format */ + } video_displayformat_t; + +as argument. + + +.. _video-stream-source-t: + +video_stream_source_t +===================== + +The video stream source is set through the VIDEO_SELECT_SOURCE call +and can take the following values, depending on whether we are replaying +from an internal (demuxer) or external (user write) source. + + +.. code-block:: c + + typedef enum { + VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ + VIDEO_SOURCE_MEMORY /* If this source is selected, the stream + comes from the user through the write + system call */ + } video_stream_source_t; + +VIDEO_SOURCE_DEMUX selects the demultiplexer (fed either by the +frontend or the DVR device) as the source of the video stream. If +VIDEO_SOURCE_MEMORY is selected the stream comes from the application +through the **write()** system call. + + +.. _video-play-state-t: + +video_play_state_t +================== + +The following values can be returned by the VIDEO_GET_STATUS call +representing the state of video playback. + + +.. code-block:: c + + typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ + } video_play_state_t; + + +.. c:type:: video_command + +struct video_command +==================== + +The structure must be zeroed before use by the application This ensures +it can be extended safely in the future. + + +.. code-block:: c + + struct video_command { + __u32 cmd; + __u32 flags; + union { + struct { + __u64 pts; + } stop; + + struct { + /* 0 or 1000 specifies normal speed, + 1 specifies forward single stepping, + -1 specifies backward single stepping, + >>1: playback at speed/1000 of the normal speed, + <-1: reverse playback at (-speed/1000) of the normal speed. */ + __s32 speed; + __u32 format; + } play; + + struct { + __u32 data[16]; + } raw; + }; + }; + + +.. _video-size-t: + +video_size_t +============ + + +.. code-block:: c + + typedef struct { + int w; + int h; + video_format_t aspect_ratio; + } video_size_t; + + +.. c:type:: video_event + +struct video_event +================== + +The following is the structure of a video event as it is returned by the +VIDEO_GET_EVENT call. + + +.. code-block:: c + + struct video_event { + __s32 type; + #define VIDEO_EVENT_SIZE_CHANGED 1 + #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 + #define VIDEO_EVENT_DECODER_STOPPED 3 + #define VIDEO_EVENT_VSYNC 4 + long timestamp; + union { + video_size_t size; + unsigned int frame_rate; /* in frames per 1000sec */ + unsigned char vsync_field; /* unknown/odd/even/progressive */ + } u; + }; + + +.. c:type:: video_status + +struct video_status +=================== + +The VIDEO_GET_STATUS call returns the following structure informing +about various states of the playback operation. + + +.. code-block:: c + + struct video_status { + int video_blank; /* blank video on freeze? */ + video_play_state_t play_state; /* current state of playback */ + video_stream_source_t stream_source; /* current source (demux/memory) */ + video_format_t video_format; /* current aspect ratio of stream */ + video_displayformat_t display_format;/* selected cropping mode */ + }; + +If video_blank is set video will be blanked out if the channel is +changed or if playback is stopped. Otherwise, the last picture will be +displayed. play_state indicates if the video is currently frozen, +stopped, or being played back. The stream_source corresponds to the +selected source for the video stream. It can come either from the +demultiplexer or from memory. The video_format indicates the aspect +ratio (one of 4:3 or 16:9) of the currently played video stream. +Finally, display_format corresponds to the selected cropping mode in +case the source video format is not the same as the format of the output +device. + + +.. c:type:: video_still_picture + +struct video_still_picture +========================== + +An I-frame displayed via the VIDEO_STILLPICTURE call is passed on +within the following structure. + + +.. code-block:: c + + /* pointer to and size of a single iframe in memory */ + struct video_still_picture { + char *iFrame; /* pointer to a single iframe in memory */ + int32_t size; + }; + + +.. _video_caps: + +video capabilities +================== + +A call to VIDEO_GET_CAPABILITIES returns an unsigned integer with the +following bits set according to the hardwares capabilities. + + +.. code-block:: c + + /* bit definitions for capabilities: */ + /* can the hardware decode MPEG1 and/or MPEG2? */ + #define VIDEO_CAP_MPEG1 1 + #define VIDEO_CAP_MPEG2 2 + /* can you send a system and/or program stream to video device? + (you still have to open the video and the audio device but only + send the stream to the video device) */ + #define VIDEO_CAP_SYS 4 + #define VIDEO_CAP_PROG 8 + /* can the driver also handle SPU, NAVI and CSS encoded data? + (CSS API is not present yet) */ + #define VIDEO_CAP_SPU 16 + #define VIDEO_CAP_NAVI 32 + #define VIDEO_CAP_CSS 64 -- cgit v1.3-14-g43fede From 211f8304fa21aaedc2c247f0c9d6c7f1aaa61ad7 Mon Sep 17 00:00:00 2001 From: Liang He Date: Wed, 20 Jul 2022 16:30:03 +0200 Subject: media: exynos4-is: fimc-is: Add of_node_put() when breaking out of loop In fimc_is_register_subdevs(), we need to call of_node_put() for the reference 'i2c_bus' when breaking out of the for_each_compatible_node() which has increased the refcount. Fixes: 9a761e436843 ("[media] exynos4-is: Add Exynos4x12 FIMC-IS driver") Signed-off-by: Liang He Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/samsung/exynos4-is/fimc-is.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index e3072d69c49f..a7704ff069d6 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -213,6 +213,7 @@ static int fimc_is_register_subdevs(struct fimc_is *is) if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { of_node_put(child); + of_node_put(i2c_bus); return ret; } index++; -- cgit v1.3-14-g43fede From 2b064d91440b33fba5b452f2d1b31f13ae911d71 Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Thu, 28 Jul 2022 04:23:38 +0200 Subject: media: cx88: Fix a null-ptr-deref bug in buffer_prepare() When the driver calls cx88_risc_buffer() to prepare the buffer, the function call may fail, resulting in a empty buffer and null-ptr-deref later in buffer_queue(). The following log can reveal it: [ 41.822762] general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN PTI [ 41.824488] KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] [ 41.828027] RIP: 0010:buffer_queue+0xc2/0x500 [ 41.836311] Call Trace: [ 41.836945] __enqueue_in_driver+0x141/0x360 [ 41.837262] vb2_start_streaming+0x62/0x4a0 [ 41.838216] vb2_core_streamon+0x1da/0x2c0 [ 41.838516] __vb2_init_fileio+0x981/0xbc0 [ 41.839141] __vb2_perform_fileio+0xbf9/0x1120 [ 41.840072] vb2_fop_read+0x20e/0x400 [ 41.840346] v4l2_read+0x215/0x290 [ 41.840603] vfs_read+0x162/0x4c0 Fix this by checking the return value of cx88_risc_buffer() [hverkuil: fix coding style issues] Signed-off-by: Zheyu Ma Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-vbi.c | 9 ++++---- drivers/media/pci/cx88/cx88-video.c | 43 +++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c index a075788c64d4..469aeaa725ad 100644 --- a/drivers/media/pci/cx88/cx88-vbi.c +++ b/drivers/media/pci/cx88/cx88-vbi.c @@ -144,11 +144,10 @@ static int buffer_prepare(struct vb2_buffer *vb) return -EINVAL; vb2_set_plane_payload(vb, 0, size); - cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl, - 0, VBI_LINE_LENGTH * lines, - VBI_LINE_LENGTH, 0, - lines); - return 0; + return cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl, + 0, VBI_LINE_LENGTH * lines, + VBI_LINE_LENGTH, 0, + lines); } static void buffer_finish(struct vb2_buffer *vb) diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index d3729be89252..b509c2a03852 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -431,6 +431,7 @@ static int queue_setup(struct vb2_queue *q, static int buffer_prepare(struct vb2_buffer *vb) { + int ret; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct cx8800_dev *dev = vb->vb2_queue->drv_priv; struct cx88_core *core = dev->core; @@ -445,35 +446,35 @@ static int buffer_prepare(struct vb2_buffer *vb) switch (core->field) { case V4L2_FIELD_TOP: - cx88_risc_buffer(dev->pci, &buf->risc, - sgt->sgl, 0, UNSET, - buf->bpl, 0, core->height); + ret = cx88_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, UNSET, + buf->bpl, 0, core->height); break; case V4L2_FIELD_BOTTOM: - cx88_risc_buffer(dev->pci, &buf->risc, - sgt->sgl, UNSET, 0, - buf->bpl, 0, core->height); + ret = cx88_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, UNSET, 0, + buf->bpl, 0, core->height); break; case V4L2_FIELD_SEQ_TB: - cx88_risc_buffer(dev->pci, &buf->risc, - sgt->sgl, - 0, buf->bpl * (core->height >> 1), - buf->bpl, 0, - core->height >> 1); + ret = cx88_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + 0, buf->bpl * (core->height >> 1), + buf->bpl, 0, + core->height >> 1); break; case V4L2_FIELD_SEQ_BT: - cx88_risc_buffer(dev->pci, &buf->risc, - sgt->sgl, - buf->bpl * (core->height >> 1), 0, - buf->bpl, 0, - core->height >> 1); + ret = cx88_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + buf->bpl * (core->height >> 1), 0, + buf->bpl, 0, + core->height >> 1); break; case V4L2_FIELD_INTERLACED: default: - cx88_risc_buffer(dev->pci, &buf->risc, - sgt->sgl, 0, buf->bpl, - buf->bpl, buf->bpl, - core->height >> 1); + ret = cx88_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, buf->bpl, + buf->bpl, buf->bpl, + core->height >> 1); break; } dprintk(2, @@ -481,7 +482,7 @@ static int buffer_prepare(struct vb2_buffer *vb) buf, buf->vb.vb2_buf.index, __func__, core->width, core->height, dev->fmt->depth, dev->fmt->fourcc, (unsigned long)buf->risc.dma); - return 0; + return ret; } static void buffer_finish(struct vb2_buffer *vb) -- cgit v1.3-14-g43fede From d682869daa23938b5e8919db45c4b5b227749712 Mon Sep 17 00:00:00 2001 From: Zeng Jingxiang Date: Thu, 28 Jul 2022 18:12:36 +0800 Subject: media: tm6000: Fix unused value in vidioc_try_fmt_vid_cap() Coverity warns of an unused value: assigned_value: Assign the value of the variable f->fmt.pix.field to field here, but that stored value is overwritten. before it can be used. 919 field = f->fmt.pix.field; 920 value_overwrite: Overwriting previous write to field with the value of V4L2_FIELD_INTERLACED. 921 field = V4L2_FIELD_INTERLACED; Fixes: ed57256f6fe8 ("[media] tm6000: fix G/TRY_FMT") Signed-off-by: Zeng Jingxiang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/deprecated/tm6000/tm6000-video.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-video.c b/drivers/staging/media/deprecated/tm6000/tm6000-video.c index d855a19551f3..e06ed21edbdd 100644 --- a/drivers/staging/media/deprecated/tm6000/tm6000-video.c +++ b/drivers/staging/media/deprecated/tm6000/tm6000-video.c @@ -916,8 +916,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - field = f->fmt.pix.field; - field = V4L2_FIELD_INTERLACED; tm6000_get_std_res(dev); -- cgit v1.3-14-g43fede From 23bc5eb55f8c9607965c20d9ddcc13cb1ae59568 Mon Sep 17 00:00:00 2001 From: Dongliang Mu Date: Thu, 11 Aug 2022 06:57:00 +0200 Subject: media: airspy: fix memory leak in airspy probe The commit ca9dc8d06ab6 ("media: airspy: respect the DMA coherency rules") moves variable buf from stack to heap, however, it only frees buf in the error handling code, missing deallocation in the success path. Fix this by freeing buf in the success path since this variable does not have any references in other code. Fixes: ca9dc8d06ab6 ("media: airspy: respect the DMA coherency rules") Reported-by: syzbot+bb25f85e5aa482864dc0@syzkaller.appspotmail.com Signed-off-by: Dongliang Mu Reviewed-by: Tommaso Merciai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/airspy/airspy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index b8b88244f963..462eb8423506 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -1070,6 +1070,10 @@ static int airspy_probe(struct usb_interface *intf, ret); goto err_free_controls; } + + /* Free buf if success*/ + kfree(buf); + dev_info(s->dev, "Registered as %s\n", video_device_node_name(&s->vdev)); dev_notice(s->dev, "SDR API is still slightly experimental and functionality changes may follow\n"); -- cgit v1.3-14-g43fede From 1c24bb3f8bec5805df4763a4327ce616fadfed65 Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Thu, 11 Aug 2022 22:37:56 +0200 Subject: media: v4l2-ctrls: Fix typo in VP8 comment The comment for the VP8 loop filter flags uses the partially wrong name for the flags. Unlike the other VP8 flag names, the loop filter flag names don't have "_FLAG" in them. Change the comment so that it matches the actual flag definitions in the header. Signed-off-by: Deborah Brouwer Reviewed-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-controls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 87fa476428ee..b5e7d082b8ad 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -1736,7 +1736,7 @@ struct v4l2_vp8_segment { * @sharpness_level: matches sharpness_level syntax element. * @level: matches loop_filter_level syntax element. * @padding: padding field. Should be zeroed by applications. - * @flags: see V4L2_VP8_LF_FLAG_{}. + * @flags: see V4L2_VP8_LF_{}. * * This structure contains loop filter related parameters. * See the 'mb_lf_adjustments()' part of the frame header syntax, -- cgit v1.3-14-g43fede From 0565b91eae14b65850f33a571d59c1cac725536c Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Mon, 15 Aug 2022 12:36:22 +0200 Subject: media: rockchip: rga: Fix probe rga_parse_dt bugs rga_parse_dt is missing a error return, so if some of the resources return DEFER_PROBE, probe will succeed without these resources. Signed-off-by: Ondrej Jirman Co-developed-by: Jarrah Gosbell Signed-off-by: Jarrah Gosbell Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 2f8df74ad0fd..61b25fcf826e 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -816,7 +816,7 @@ static int rga_probe(struct platform_device *pdev) ret = rga_parse_dt(rga); if (ret) - dev_err(&pdev->dev, "Unable to parse OF data\n"); + return dev_err_probe(&pdev->dev, ret, "Unable to parse OF data\n"); pm_runtime_enable(rga->dev); -- cgit v1.3-14-g43fede From 17611d3fb4a11ec500c49cb952faf09e114a5a10 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Aug 2022 09:51:40 +0200 Subject: media: videodev2.h: drop V4L2_CAP_ASYNCIO The V4L2_CAP_ASYNCIO capability was never implemented (and in fact it isn't clear what it was supposed to do in the first place). Drop it from the capabilities list. Keep it in videodev2.h with the other defines under ifndef __KERNEL__ for backwards compatibility. This will free up a capability bit for other future uses. And having an unused and undefined I/O method is just plain confusing. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/async.rst | 9 --------- Documentation/userspace-api/media/v4l/dev-raw-vbi.rst | 2 +- Documentation/userspace-api/media/v4l/dev-sdr.rst | 2 +- Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst | 2 +- Documentation/userspace-api/media/v4l/hist-v4l2.rst | 2 +- Documentation/userspace-api/media/v4l/io.rst | 4 +--- Documentation/userspace-api/media/v4l/vidioc-querycap.rst | 3 --- include/uapi/linux/videodev2.h | 6 +++++- 8 files changed, 10 insertions(+), 20 deletions(-) delete mode 100644 Documentation/userspace-api/media/v4l/async.rst diff --git a/Documentation/userspace-api/media/v4l/async.rst b/Documentation/userspace-api/media/v4l/async.rst deleted file mode 100644 index d6960ff5c382..000000000000 --- a/Documentation/userspace-api/media/v4l/async.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _async: - -**************** -Asynchronous I/O -**************** - -This method is not defined yet. diff --git a/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst b/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst index 58f97c3a7792..2bec20d87928 100644 --- a/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst +++ b/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst @@ -41,7 +41,7 @@ Devices supporting the raw VBI capturing or output API set the in the ``capabilities`` field of struct :c:type:`v4l2_capability` returned by the :ref:`VIDIOC_QUERYCAP` ioctl. At least one of the -read/write, streaming or asynchronous I/O methods must be supported. VBI +read/write or streaming I/O methods must be supported. VBI devices may or may not have a tuner or modulator. Supplemental Functions diff --git a/Documentation/userspace-api/media/v4l/dev-sdr.rst b/Documentation/userspace-api/media/v4l/dev-sdr.rst index 928884dfe09d..dfdeddbca41f 100644 --- a/Documentation/userspace-api/media/v4l/dev-sdr.rst +++ b/Documentation/userspace-api/media/v4l/dev-sdr.rst @@ -34,7 +34,7 @@ Devices supporting the SDR transmitter interface set the device has an Digital to Analog Converter (DAC), which is a mandatory element for the SDR transmitter. -At least one of the read/write, streaming or asynchronous I/O methods +At least one of the read/write or streaming I/O methods must be supported. diff --git a/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst b/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst index 97ec2b115c71..44415822c7c5 100644 --- a/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst +++ b/Documentation/userspace-api/media/v4l/dev-sliced-vbi.rst @@ -36,7 +36,7 @@ Devices supporting the sliced VBI capturing or output API set the respectively, in the ``capabilities`` field of struct :c:type:`v4l2_capability` returned by the :ref:`VIDIOC_QUERYCAP` ioctl. At least one of the -read/write, streaming or asynchronous :ref:`I/O methods ` must be +read/write or streaming :ref:`I/O methods ` must be supported. Sliced VBI devices may have a tuner or modulator. Supplemental Functions diff --git a/Documentation/userspace-api/media/v4l/hist-v4l2.rst b/Documentation/userspace-api/media/v4l/hist-v4l2.rst index 28a2750d5c8c..dbc04374dc22 100644 --- a/Documentation/userspace-api/media/v4l/hist-v4l2.rst +++ b/Documentation/userspace-api/media/v4l/hist-v4l2.rst @@ -316,7 +316,7 @@ This unnamed version was finally merged into Linux 2.5.46. There are new fields to identify the driver, a new RDS device function ``V4L2_CAP_RDS_CAPTURE``, the ``V4L2_CAP_AUDIO`` flag indicates if the device has any audio connectors, another I/O - capability ``V4L2_CAP_ASYNCIO`` can be flagged. In response to these + capability V4L2_CAP_ASYNCIO can be flagged. In response to these changes the ``type`` field became a bit set and was merged into the ``flags`` field. ``V4L2_FLAG_TUNER`` was renamed to ``V4L2_CAP_TUNER``, ``V4L2_CAP_VIDEO_OVERLAY`` replaced diff --git a/Documentation/userspace-api/media/v4l/io.rst b/Documentation/userspace-api/media/v4l/io.rst index ce0cece6f35f..4b1964df9d73 100644 --- a/Documentation/userspace-api/media/v4l/io.rst +++ b/Documentation/userspace-api/media/v4l/io.rst @@ -17,8 +17,7 @@ read or write will fail at any time. Other methods must be negotiated. To select the streaming I/O method with memory mapped or user buffers applications call the -:ref:`VIDIOC_REQBUFS` ioctl. The asynchronous I/O -method is not defined yet. +:ref:`VIDIOC_REQBUFS` ioctl. Video overlay can be considered another I/O method, although the application does not directly receive the image data. It is selected by @@ -46,6 +45,5 @@ The following sections describe the various I/O methods in more detail. mmap userp dmabuf - async buffer field-order diff --git a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst index 63e23f6f95ee..6c57b8428356 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst @@ -244,9 +244,6 @@ specification the ioctl returns an ``EINVAL`` error code. - 0x01000000 - The device supports the :c:func:`read()` and/or :c:func:`write()` I/O methods. - * - ``V4L2_CAP_ASYNCIO`` - - 0x02000000 - - The device supports the :ref:`asynchronous ` I/O methods. * - ``V4L2_CAP_STREAMING`` - 0x04000000 - The device supports the :ref:`streaming ` I/O method. diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c415ce5b6829..86cae23cc446 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -502,7 +502,6 @@ struct v4l2_capability { #define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */ #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ -#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ #define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */ @@ -2683,6 +2682,11 @@ struct v4l2_create_buffers { #ifndef __KERNEL__ #define V4L2_PIX_FMT_HM12 V4L2_PIX_FMT_NV12_16L16 #define V4L2_PIX_FMT_SUNXI_TILED_NV12 V4L2_PIX_FMT_NV12_32L32 +/* + * This capability was never implemented, anyone using this cap should drop it + * from their code. + */ +#define V4L2_CAP_ASYNCIO 0x02000000 #endif #endif /* _UAPI__LINUX_VIDEODEV2_H */ -- cgit v1.3-14-g43fede From a7985e3cecd56d0f553487ac12fe4f65962c4b34 Mon Sep 17 00:00:00 2001 From: Daniel Lee Kruse Date: Fri, 19 Aug 2022 05:59:18 +0200 Subject: media: cx23885: reset DMA on AMD Renior/Cezanne IOMMU due to RiSC engine stall MythTv is unable to scan channels with APUs with the Renior IOMMU that is also contained in the Cezanne line of APUs. This issue was discovered on the 5.15 version the kernel. This patch adds the IOMMU PCI ID to the broken_dev_id array. This patch was developed with 5.19 of the media_tree repo. [hverkuil: cleaned up the commit log a bit] Signed-off-by: Daniel Lee Kruse Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index a07b18f2034e..9232a966bcab 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2086,6 +2086,9 @@ static struct { /* 0x1419 is the PCI ID for the IOMMU found on 15h (Models 10h-1fh) family */ { PCI_VENDOR_ID_AMD, 0x1419 }, + /* 0x1631 is the PCI ID for the IOMMU found on Renoir/Cezanne + */ + { PCI_VENDOR_ID_AMD, 0x1631 }, /* 0x5a23 is the PCI ID for the IOMMU found on RD890S/RD990 */ { PCI_VENDOR_ID_ATI, 0x5a23 }, -- cgit v1.3-14-g43fede From 8e69714e6177d963668a45499b500c1d4de7bac8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 22 Aug 2022 11:40:43 +0200 Subject: media: dw100: Fix an error handling path in dw100_probe() After a successful call to media_device_init() it is safer to call media_device_cleanup(). Add the missing call in the error handling path of the probe, as already done in the remove function. [hverkuil: fixed a typo in the commit log] Fixes: bd090d4d995a ("media: dw100: Add i.MX8MP dw100 dewarper driver") Signed-off-by: Christophe JAILLET Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/nxp/dw100/dw100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index 94518f0e486b..b3b057798ab6 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -1623,6 +1623,7 @@ error_m2m_mc: error_v4l2: video_unregister_device(vfd); err_m2m: + media_device_cleanup(&dw_dev->mdev); v4l2_m2m_release(dw_dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dw_dev->v4l2_dev); -- cgit v1.3-14-g43fede From 107ca18cad69162fd2750c4616fa1cc02b15394d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Aug 2022 08:20:46 +0200 Subject: media: MAINTAINERS: change tc358743 maintainer Move maintainer from Mats to Hans. Add bindings file to the list of maintained files while we're at it. Signed-off-by: Hans Verkuil Signed-off-by: Mats Randgaard Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index d457299ce736..fee641d10847 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20569,9 +20569,10 @@ F: include/linux/toshiba.h F: include/uapi/linux/toshiba.h TOSHIBA TC358743 DRIVER -M: Mats Randgaard +M: Hans Verkuil L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/i2c/tc358743.txt F: drivers/media/i2c/tc358743* F: include/media/i2c/tc358743.h -- cgit v1.3-14-g43fede From 05c480f4d0ba438e6d37a1660d81cc7eb670e26b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Aug 2022 08:57:26 +0200 Subject: media: media/cec: use CEC_MAX_MSG_SIZE instead of hardcoded 16 Use the proper define for the maximum CEC message length instead of hardcoding it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/platform/sti/stih-cec.c | 4 ++-- drivers/media/i2c/adv7511-v4l2.c | 4 ++-- drivers/media/i2c/adv7604.c | 4 ++-- drivers/media/i2c/adv7842.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/cec/platform/sti/stih-cec.c b/drivers/media/cec/platform/sti/stih-cec.c index abf8e8bcbb34..4edbdd09535d 100644 --- a/drivers/media/cec/platform/sti/stih-cec.c +++ b/drivers/media/cec/platform/sti/stih-cec.c @@ -256,8 +256,8 @@ static void stih_rx_done(struct stih_cec *cec, u32 status) if (!msg.len) return; - if (msg.len > 16) - msg.len = 16; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; for (i = 0; i < msg.len; i++) msg.msg[i] = readl(cec->regs + CEC_RX_DATA_BASE + i); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 202e0cd83f90..910b5e83946a 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -943,8 +943,8 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__, msg.len); - if (msg.len > 16) - msg.len = 16; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; if (msg.len) { u8 i; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 497419a5cfdd..815fe108281f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2047,8 +2047,8 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) struct cec_msg msg; msg.len = cec_read(sd, 0x25) & 0x1f; - if (msg.len > 16) - msg.len = 16; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; if (msg.len) { u8 i; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 22caa070273b..c541f9b3444b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2215,8 +2215,8 @@ static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled) struct cec_msg msg; msg.len = cec_read(sd, 0x25) & 0x1f; - if (msg.len > 16) - msg.len = 16; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; if (msg.len) { u8 i; -- cgit v1.3-14-g43fede From 3028fb90f5b972ef9b9e68e75a8372b8f9a8e312 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 24 Aug 2022 09:00:24 +0200 Subject: media: tc358743: limit msg.len to CEC_MAX_MSG_SIZE I expect that the hardware will have limited this to 16, but just in case it hasn't, check for this corner case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index e18b8947ad7e..804b8ee8e4ab 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -964,6 +964,8 @@ static void tc358743_cec_handler(struct v4l2_subdev *sd, u16 intstatus, v = i2c_rd32(sd, CECRCTR); msg.len = v & 0x1f; + if (msg.len > CEC_MAX_MSG_SIZE) + msg.len = CEC_MAX_MSG_SIZE; for (i = 0; i < msg.len; i++) { v = i2c_rd32(sd, CECRBUF1 + i * 4); msg.msg[i] = v & 0xff; -- cgit v1.3-14-g43fede From 2a50e8d75abaa9f878ec424c3d0a0c8d98767d0b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 26 Apr 2022 09:06:14 +0200 Subject: media: ti: cal: fix useless variable init 'ret' is initialized needlessly in cal_legacy_try_fmt_vid_cap(). We can also move the variable to the for block, which is the only place where it is used. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/cal/cal-video.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 776da0cfcdbe..21e3d0aabf70 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -191,7 +191,7 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, struct cal_ctx *ctx = video_drvdata(file); const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; - int ret, found; + int found; fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); if (!fmtinfo) { @@ -206,12 +206,13 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; /* check for/find a valid width/height */ - ret = 0; found = false; fse.pad = 0; fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { + int ret; + ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, &fse); if (ret) -- cgit v1.3-14-g43fede From b1b93d36c385436742ce4ddf412faaa0383529a5 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 26 Apr 2022 09:06:15 +0200 Subject: media: ti: cal: rename sd_state to state Rename 'sd_state' parameters to 'state'. There are no other states, so there is no ambiguity. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/cal/cal-camerarx.c | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index e136d70b4048..a5d9189ae6e3 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -622,12 +622,12 @@ static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) static struct v4l2_mbus_framefmt * cal_camerarx_get_pad_format(struct cal_camerarx *phy, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&phy->subdev, sd_state, pad); + return v4l2_subdev_get_try_format(&phy->subdev, state, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &phy->formats[pad]; default: @@ -653,7 +653,7 @@ static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) } static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { struct cal_camerarx *phy = to_cal_camerarx(sd); @@ -670,7 +670,7 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, goto out; } - fmt = cal_camerarx_get_pad_format(phy, sd_state, + fmt = cal_camerarx_get_pad_format(phy, state, CAL_CAMERARX_PAD_SINK, code->which); code->code = fmt->code; @@ -690,7 +690,7 @@ out: } static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { struct cal_camerarx *phy = to_cal_camerarx(sd); @@ -706,7 +706,7 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = cal_camerarx_get_pad_format(phy, sd_state, + fmt = cal_camerarx_get_pad_format(phy, state, CAL_CAMERARX_PAD_SINK, fse->which); if (fse->code != fmt->code) { @@ -738,7 +738,7 @@ out: } static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct cal_camerarx *phy = to_cal_camerarx(sd); @@ -746,7 +746,7 @@ static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, mutex_lock(&phy->mutex); - fmt = cal_camerarx_get_pad_format(phy, sd_state, format->pad, + fmt = cal_camerarx_get_pad_format(phy, state, format->pad, format->which); format->format = *fmt; @@ -756,7 +756,7 @@ static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, } static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct cal_camerarx *phy = to_cal_camerarx(sd); @@ -766,7 +766,7 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(format->pad)) - return cal_camerarx_sd_get_fmt(sd, sd_state, format); + return cal_camerarx_sd_get_fmt(sd, state, format); /* * Default to the first format if the requested media bus code isn't @@ -792,12 +792,12 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, mutex_lock(&phy->mutex); - fmt = cal_camerarx_get_pad_format(phy, sd_state, + fmt = cal_camerarx_get_pad_format(phy, state, CAL_CAMERARX_PAD_SINK, format->which); *fmt = format->format; - fmt = cal_camerarx_get_pad_format(phy, sd_state, + fmt = cal_camerarx_get_pad_format(phy, state, CAL_CAMERARX_PAD_FIRST_SOURCE, format->which); *fmt = format->format; @@ -808,10 +808,10 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, } static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) + struct v4l2_subdev_state *state) { struct v4l2_subdev_format format = { - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + .which = state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE, .pad = CAL_CAMERARX_PAD_SINK, .format = { @@ -826,7 +826,7 @@ static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, }, }; - return cal_camerarx_sd_set_fmt(sd, sd_state, &format); + return cal_camerarx_sd_set_fmt(sd, state, &format); } static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { -- cgit v1.3-14-g43fede From ebdb28a55da1248d6dfe3bcde9c9f6198f770b2d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 26 Apr 2022 09:06:16 +0200 Subject: media: ti: cal: use CSI-2 frame number for seq number The userspace needs a way to match received metadata buffers to pixel data buffers. The obvious way to do this is to use the CSI-2 frame number, as both the metadata and the pixel data have the same frame number as they come from the same frame. However, we don't have means to convey the frame number to userspace. We do have the 'sequence' field, which with a few tricks can be used for this purpose. To achieve this, track the frame number for each virtual channel and increase the sequence for each virtual channel by frame-number - previous-frame-number, also taking into account the eventual wrap of the CSI-2 frame number. If the CSI-2 peripheral does not support frame numbers, CAL increases the frame number register by one each frame. This way we get a monotonically increasing sequence number which is common to all streams using the same virtual channel. Signed-off-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/cal/cal-camerarx.c | 1 + drivers/media/platform/ti/cal/cal.c | 57 +++++++++++++++++++++++++++- drivers/media/platform/ti/cal/cal.h | 7 +++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index a5d9189ae6e3..16ae52879a79 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -871,6 +871,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + spin_lock_init(&phy->vc_lock); mutex_init(&phy->mutex); phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 425b4f4b7ed7..afba0b72a68c 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -543,7 +543,22 @@ void cal_ctx_unprepare(struct cal_ctx *ctx) void cal_ctx_start(struct cal_ctx *ctx) { - ctx->sequence = 0; + struct cal_camerarx *phy = ctx->phy; + + /* + * Reset the frame number & sequence number, but only if the + * virtual channel is not already in use. + */ + + spin_lock(&phy->vc_lock); + + if (phy->vc_enable_count[ctx->vc]++ == 0) { + phy->vc_frame_number[ctx->vc] = 0; + phy->vc_sequence[ctx->vc] = 0; + } + + spin_unlock(&phy->vc_lock); + ctx->dma.state = CAL_DMA_RUNNING; /* Configure the CSI-2, pixel processing and write DMA contexts. */ @@ -563,8 +578,15 @@ void cal_ctx_start(struct cal_ctx *ctx) void cal_ctx_stop(struct cal_ctx *ctx) { + struct cal_camerarx *phy = ctx->phy; long timeout; + WARN_ON(phy->vc_enable_count[ctx->vc] == 0); + + spin_lock(&phy->vc_lock); + phy->vc_enable_count[ctx->vc]--; + spin_unlock(&phy->vc_lock); + /* * Request DMA stop and wait until it completes. If completion times * out, forcefully disable the DMA. @@ -601,6 +623,34 @@ void cal_ctx_stop(struct cal_ctx *ctx) * ------------------------------------------------------------------ */ +/* + * Track a sequence number for each virtual channel, which is shared by + * all contexts using the same virtual channel. This is done using the + * CSI-2 frame number as a base. + */ +static void cal_update_seq_number(struct cal_ctx *ctx) +{ + struct cal_dev *cal = ctx->cal; + struct cal_camerarx *phy = ctx->phy; + u16 prev_frame_num, frame_num; + u8 vc = ctx->vc; + + frame_num = + cal_read(cal, CAL_CSI2_STATUS(phy->instance, ctx->csi2_ctx)) & + 0xffff; + + if (phy->vc_frame_number[vc] != frame_num) { + prev_frame_num = phy->vc_frame_number[vc]; + + if (prev_frame_num >= frame_num) + phy->vc_sequence[vc] += 1; + else + phy->vc_sequence[vc] += frame_num - prev_frame_num; + + phy->vc_frame_number[vc] = frame_num; + } +} + static inline void cal_irq_wdma_start(struct cal_ctx *ctx) { spin_lock(&ctx->dma.lock); @@ -631,6 +681,8 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) } spin_unlock(&ctx->dma.lock); + + cal_update_seq_number(ctx); } static inline void cal_irq_wdma_end(struct cal_ctx *ctx) @@ -657,7 +709,8 @@ static inline void cal_irq_wdma_end(struct cal_ctx *ctx) if (buf) { buf->vb.vb2_buf.timestamp = ktime_get_ns(); buf->vb.field = ctx->v_fmt.fmt.pix.field; - buf->vb.sequence = ctx->sequence++; + buf->vb.sequence = ctx->phy->vc_sequence[ctx->vc]; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } } diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h index 61409ddced98..80f2c9c73c71 100644 --- a/drivers/media/platform/ti/cal/cal.h +++ b/drivers/media/platform/ti/cal/cal.h @@ -180,6 +180,12 @@ struct cal_camerarx { struct media_pad pads[CAL_CAMERARX_NUM_PADS]; struct v4l2_mbus_framefmt formats[CAL_CAMERARX_NUM_PADS]; + /* protects the vc_* fields below */ + spinlock_t vc_lock; + u8 vc_enable_count[4]; + u16 vc_frame_number[4]; + u32 vc_sequence[4]; + /* * Lock for camerarx ops. Protects: * - formats @@ -242,7 +248,6 @@ struct cal_ctx { const struct cal_format_info **active_fmt; unsigned int num_active_fmt; - unsigned int sequence; struct vb2_queue vb_vidq; u8 dma_ctx; u8 cport; -- cgit v1.3-14-g43fede From 0872dc04cf65fdfaa1d67453d535f8181982848b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 26 Apr 2022 09:06:17 +0200 Subject: media: ti: cal: combine wdma irq handling Instead of handling the WDMA START and END interrupts separately, we need to handle both at the same time to better manage the inherent race conditions related to CAL interrupts. Change the code so that we have a single function, cal_irq_handle_wdma(), which gets two booleans, start and end, as parameters, which allows us to manage the race conditions in the following patch. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/cal/cal.c | 59 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index afba0b72a68c..910ff179e597 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -715,22 +715,33 @@ static inline void cal_irq_wdma_end(struct cal_ctx *ctx) } } +static void cal_irq_handle_wdma(struct cal_ctx *ctx, bool start, bool end) +{ + if (end) + cal_irq_wdma_end(ctx); + + if (start) + cal_irq_wdma_start(ctx); +} + static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; - u32 status; - - status = cal_read(cal, CAL_HL_IRQSTATUS(0)); - if (status) { - unsigned int i; + u32 status[3]; + unsigned int i; - cal_write(cal, CAL_HL_IRQSTATUS(0), status); + for (i = 0; i < 3; ++i) { + status[i] = cal_read(cal, CAL_HL_IRQSTATUS(i)); + if (status[i]) + cal_write(cal, CAL_HL_IRQSTATUS(i), status[i]); + } - if (status & CAL_HL_IRQ_OCPO_ERR_MASK) + if (status[0]) { + if (status[0] & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < cal->data->num_csi2_phy; ++i) { - if (status & CAL_HL_IRQ_CIO_MASK(i)) { + if (status[0] & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -741,7 +752,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cio_stat); } - if (status & CAL_HL_IRQ_VC_MASK(i)) { + if (status[0] & CAL_HL_IRQ_VC_MASK(i)) { u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i)); dev_err_ratelimited(cal->dev, @@ -753,32 +764,12 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } } - /* Check which DMA just finished */ - status = cal_read(cal, CAL_HL_IRQSTATUS(1)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(1), status); - - for (i = 0; i < cal->num_contexts; ++i) { - if (status & CAL_HL_IRQ_WDMA_END_MASK(i)) - cal_irq_wdma_end(cal->ctx[i]); - } - } - - /* Check which DMA just started */ - status = cal_read(cal, CAL_HL_IRQSTATUS(2)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(2), status); + for (i = 0; i < cal->num_contexts; ++i) { + bool end = !!(status[1] & CAL_HL_IRQ_WDMA_END_MASK(i)); + bool start = !!(status[2] & CAL_HL_IRQ_WDMA_START_MASK(i)); - for (i = 0; i < cal->num_contexts; ++i) { - if (status & CAL_HL_IRQ_WDMA_START_MASK(i)) - cal_irq_wdma_start(cal->ctx[i]); - } + if (start || end) + cal_irq_handle_wdma(cal->ctx[i], start, end); } return IRQ_HANDLED; -- cgit v1.3-14-g43fede From d91105e0567dabf487f81019e8b609706334d5ed Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 26 Apr 2022 09:06:18 +0200 Subject: media: ti: cal: fix wdma irq for metadata CAL HW interrupts are inherently racy. If we get both start and end interrupts, we don't know what has happened: did the DMA for a single frame start and end, or did one frame end and a new frame start? Usually for normal pixel frames we get the interrupts separately. If we do get both, we have to guess. The assumption in the code is that the active vertical area is larger than the blanking vertical area, and thus it is more likely that we get the end of the old frame and the start of a new frame. However, for embedded data, which is only a few lines high, we always get both interrupts. Here the assumption is that we get both for the same frame. Signed-off-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/cal/cal.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 910ff179e597..56b61c0583cf 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -717,11 +717,34 @@ static inline void cal_irq_wdma_end(struct cal_ctx *ctx) static void cal_irq_handle_wdma(struct cal_ctx *ctx, bool start, bool end) { - if (end) - cal_irq_wdma_end(ctx); + /* + * CAL HW interrupts are inherently racy. If we get both start and end + * interrupts, we don't know what has happened: did the DMA for a single + * frame start and end, or did one frame end and a new frame start? + * + * Usually for normal pixel frames we get the interrupts separately. If + * we do get both, we have to guess. The assumption in the code below is + * that the active vertical area is larger than the blanking vertical + * area, and thus it is more likely that we get the end of the old frame + * and the start of a new frame. + * + * However, for embedded data, which is only a few lines high, we always + * get both interrupts. Here the assumption is that we get both for the + * same frame. + */ + if (ctx->v_fmt.fmt.pix.height < 10) { + if (start) + cal_irq_wdma_start(ctx); - if (start) - cal_irq_wdma_start(ctx); + if (end) + cal_irq_wdma_end(ctx); + } else { + if (end) + cal_irq_wdma_end(ctx); + + if (start) + cal_irq_wdma_start(ctx); + } } static irqreturn_t cal_irq(int irq_cal, void *data) -- cgit v1.3-14-g43fede From 34cb6947bfc9ea43dde303210b98c57e1aedcb92 Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:26 +0200 Subject: media: mediatek: vcodec: Add encoder driver support for 34-bit iova Encoder driver got iova from IOMMU is 34-bit, for example: Here is the sample code: encoder input frame buffer dma address is: frm_buf = vb2_dma_contig_plane_dma_addr(&vb2_v4l2_buffer->vb2_buf, 0); the value of frm_buf is 0x1_ff30_0000. encoder driver got the frm_buf and send the iova to SCP firmware through SCP IPI message, then write to encoder hardware in SCP. The iova is stored in IPI message as uint32_t data type, so the value will be truncated from *0x1_ff30_0000* to *0xff30_0000*, and then *0xff30_0000* will be written to encoder hardware, but IOMMU will help to add the high *0x1_* bit back, so IOMMU can translate the iova to PA correctly, encoder hardware can access the correct memory for encoding. Another reason to do this is the encoder hardware can't access the 34-bit iova, IOMMU will help to add the remaining high bits of iova. But for mt8188, encoder hardware can access 34-bit iova directly, and encoder driver need write all 34 bits because IOMMU can't help driver do this if the hardware support access 34-bit iova. For the reasons above, this patch is added to support transfer 34-bit iova between kernel and SCP encoder driver. Use uint64_t data type to store the iova, for compatibility with old chipsets, add some new struct definitions for 34-bit. [hverkuil: initialize wb and wb_34 to NULL] Signed-off-by: Irui Wang Reported-by: kernel test robot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../platform/mediatek/vcodec/mtk_vcodec_drv.h | 3 + .../platform/mediatek/vcodec/venc/venc_h264_if.c | 200 +++++++++++++++++---- .../media/platform/mediatek/vcodec/venc_ipi_msg.h | 24 +++ .../media/platform/mediatek/vcodec/venc_vpu_if.c | 76 +++++++- 4 files changed, 266 insertions(+), 37 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h index ef4584a46417..fe8ed6a456e0 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h @@ -401,6 +401,7 @@ struct mtk_vcodec_dec_pdata { * @output_formats: array of supported output formats * @num_output_formats: number of entries in output_formats * @core_id: stand for h264 or vp8 encode index + * @uses_34bit: whether the encoder uses 34-bit iova */ struct mtk_vcodec_enc_pdata { bool uses_ext; @@ -411,9 +412,11 @@ struct mtk_vcodec_enc_pdata { const struct mtk_video_fmt *output_formats; size_t num_output_formats; int core_id; + bool uses_34bit; }; #define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) +#define MTK_ENC_IOVA_IS_34BIT(ctx) ((ctx)->dev->venc_pdata->uses_34bit) /** * struct mtk_vcodec_dev - driver data diff --git a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c index 4d9b8798dffe..13c4f860fa69 100644 --- a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c @@ -127,6 +127,72 @@ struct venc_h264_vsi { struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; }; +/** + * struct venc_h264_vpu_config_ext - Structure for h264 encoder configuration + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to + * hardware requirements. + * @buf_h: buffer height + * @gop_size: group of picture size (idr frame) + * @intra_period: intra frame period + * @framerate: frame rate in fps + * @profile: as specified in standard + * @level: as specified in standard + * @wfd: WFD mode 1:on, 0:off + * @max_qp: max quant parameter + * @min_qp: min quant parameter + * @reserved: reserved configs + */ +struct venc_h264_vpu_config_ext { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 intra_period; + u32 framerate; + u32 profile; + u32 level; + u32 wfd; + u32 max_qp; + u32 min_qp; + u32 reserved[8]; +}; + +/** + * struct venc_h264_vpu_buf_34 - Structure for 34-bit buffer information + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @iova: 34-bit IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_h264_vpu_buf_34 { + u64 iova; + u32 vpua; + u32 size; +}; + +/** + * struct venc_h264_vsi_34 - Structure for VPU driver control and info share + * Used for 34-bit iova sharing + * @config: h264 encoder configuration + * @work_bufs: working buffer information in VPU side + */ +struct venc_h264_vsi_34 { + struct venc_h264_vpu_config_ext config; + struct venc_h264_vpu_buf_34 work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; +}; + /* * struct venc_h264_inst - h264 encoder AP driver instance * @hw_base: h264 encoder hardware register base @@ -140,6 +206,8 @@ struct venc_h264_vsi { * @vpu_inst: VPU instance to exchange information between AP and VPU * @vsi: driver structure allocated by VPU side and shared to AP side for * control and info share + * @vsi_34: driver structure allocated by VPU side and shared to AP side for + * control and info share, used for 34-bit iova sharing. * @ctx: context for v4l2 layer integration */ struct venc_h264_inst { @@ -152,6 +220,7 @@ struct venc_h264_inst { unsigned int prepend_hdr; struct venc_vpu_inst vpu_inst; struct venc_h264_vsi *vsi; + struct venc_h264_vsi_34 *vsi_34; struct mtk_vcodec_ctx *ctx; }; @@ -244,14 +313,21 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst) mtk_vcodec_debug_leave(inst); } -static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) +static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) { + struct venc_h264_vpu_buf *wb = NULL; + struct venc_h264_vpu_buf_34 *wb_34 = NULL; int i; + u32 vpua, wb_size; int ret = 0; - struct venc_h264_vpu_buf *wb = inst->vsi->work_bufs; mtk_vcodec_debug_enter(inst); + if (is_34bit) + wb_34 = inst->vsi_34->work_bufs; + else + wb = inst->vsi->work_bufs; + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { /* * This 'wb' structure is set by VPU side and shared to AP for @@ -269,13 +345,22 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) * address and do some memcpy access to move to bitstream buffer * assigned by v4l2 layer. */ - inst->work_bufs[i].size = wb[i].size; + if (is_34bit) { + inst->work_bufs[i].size = wb_34[i].size; + vpua = wb_34[i].vpua; + wb_size = wb_34[i].size; + } else { + inst->work_bufs[i].size = wb[i].size; + vpua = wb[i].vpua; + wb_size = wb[i].size; + } + if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) { struct mtk_vcodec_fw *handler; handler = inst->vpu_inst.ctx->dev->fw_handler; inst->work_bufs[i].va = - mtk_vcodec_fw_map_dm_addr(handler, wb[i].vpua); + mtk_vcodec_fw_map_dm_addr(handler, vpua); inst->work_bufs[i].dma_addr = 0; } else { ret = mtk_vcodec_mem_alloc(inst->ctx, @@ -297,12 +382,14 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) handler = inst->vpu_inst.ctx->dev->fw_handler; tmp_va = mtk_vcodec_fw_map_dm_addr(handler, - wb[i].vpua); - memcpy(inst->work_bufs[i].va, tmp_va, - wb[i].size); + vpua); + memcpy(inst->work_bufs[i].va, tmp_va, wb_size); } } - wb[i].iova = inst->work_bufs[i].dma_addr; + if (is_34bit) + wb_34[i].iova = inst->work_bufs[i].dma_addr; + else + wb[i].iova = inst->work_bufs[i].dma_addr; mtk_vcodec_debug(inst, "work_buf[%d] va=0x%p iova=%pad size=%zu", @@ -342,22 +429,22 @@ static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) return irq_status; } -static int h264_frame_type(struct venc_h264_inst *inst) +static int h264_frame_type(unsigned int frm_cnt, unsigned int gop_size, + unsigned int intra_period) { - if ((inst->vsi->config.gop_size != 0 && - (inst->frm_cnt % inst->vsi->config.gop_size) == 0) || - (inst->frm_cnt == 0 && inst->vsi->config.gop_size == 0)) { + if ((gop_size != 0 && (frm_cnt % gop_size) == 0) || + (frm_cnt == 0 && gop_size == 0)) { /* IDR frame */ return VENC_H264_IDR_FRM; - } else if ((inst->vsi->config.intra_period != 0 && - (inst->frm_cnt % inst->vsi->config.intra_period) == 0) || - (inst->frm_cnt == 0 && inst->vsi->config.intra_period == 0)) { + } else if ((intra_period != 0 && (frm_cnt % intra_period) == 0) || + (frm_cnt == 0 && intra_period == 0)) { /* I frame */ return VENC_H264_I_FRM; } else { return VENC_H264_P_FRM; /* Note: B frames are not supported */ } } + static int h264_encode_sps(struct venc_h264_inst *inst, struct mtk_vcodec_mem *bs_buf, unsigned int *bs_size) @@ -438,18 +525,32 @@ static int h264_encode_frame(struct venc_h264_inst *inst, unsigned int *bs_size) { int ret = 0; + unsigned int gop_size; + unsigned int intra_period; unsigned int irq_status; struct venc_frame_info frame_info; + struct mtk_vcodec_ctx *ctx = inst->ctx; mtk_vcodec_debug_enter(inst); mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt); + + if (MTK_ENC_IOVA_IS_34BIT(ctx)) { + gop_size = inst->vsi_34->config.gop_size; + intra_period = inst->vsi_34->config.intra_period; + } else { + gop_size = inst->vsi->config.gop_size; + intra_period = inst->vsi->config.intra_period; + } frame_info.frm_count = inst->frm_cnt; frame_info.skip_frm_count = inst->skip_frm_cnt; - frame_info.frm_type = h264_frame_type(inst); + frame_info.frm_type = h264_frame_type(inst->frm_cnt, gop_size, + intra_period); mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", frame_info.frm_count, frame_info.skip_frm_count, frame_info.frm_type); - ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, bs_buf, &frame_info); + + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, + frm_buf, bs_buf, &frame_info); if (ret) return ret; @@ -517,7 +618,10 @@ static int h264_enc_init(struct mtk_vcodec_ctx *ctx) ret = vpu_enc_init(&inst->vpu_inst); - inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; + if (MTK_ENC_IOVA_IS_34BIT(ctx)) + inst->vsi_34 = (struct venc_h264_vsi_34 *)inst->vpu_inst.vsi; + else + inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; mtk_vcodec_debug_leave(inst); @@ -624,31 +728,61 @@ encode_err: return ret; } +static void h264_enc_set_vsi_configs(struct venc_h264_inst *inst, + struct venc_enc_param *enc_prm) +{ + inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi->config.bitrate = enc_prm->bitrate; + inst->vsi->config.pic_w = enc_prm->width; + inst->vsi->config.pic_h = enc_prm->height; + inst->vsi->config.buf_w = enc_prm->buf_width; + inst->vsi->config.buf_h = enc_prm->buf_height; + inst->vsi->config.gop_size = enc_prm->gop_size; + inst->vsi->config.framerate = enc_prm->frm_rate; + inst->vsi->config.intra_period = enc_prm->intra_period; + inst->vsi->config.profile = + h264_get_profile(inst, enc_prm->h264_profile); + inst->vsi->config.level = + h264_get_level(inst, enc_prm->h264_level); + inst->vsi->config.wfd = 0; +} + +static void h264_enc_set_vsi_34_configs(struct venc_h264_inst *inst, + struct venc_enc_param *enc_prm) +{ + inst->vsi_34->config.input_fourcc = enc_prm->input_yuv_fmt; + inst->vsi_34->config.bitrate = enc_prm->bitrate; + inst->vsi_34->config.pic_w = enc_prm->width; + inst->vsi_34->config.pic_h = enc_prm->height; + inst->vsi_34->config.buf_w = enc_prm->buf_width; + inst->vsi_34->config.buf_h = enc_prm->buf_height; + inst->vsi_34->config.gop_size = enc_prm->gop_size; + inst->vsi_34->config.framerate = enc_prm->frm_rate; + inst->vsi_34->config.intra_period = enc_prm->intra_period; + inst->vsi_34->config.profile = + h264_get_profile(inst, enc_prm->h264_profile); + inst->vsi_34->config.level = + h264_get_level(inst, enc_prm->h264_level); + inst->vsi_34->config.wfd = 0; +} + static int h264_enc_set_param(void *handle, enum venc_set_param_type type, struct venc_enc_param *enc_prm) { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + const bool is_34bit = MTK_ENC_IOVA_IS_34BIT(ctx); mtk_vcodec_debug(inst, "->type=%d", type); switch (type) { case VENC_SET_PARAM_ENC: - inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt; - inst->vsi->config.bitrate = enc_prm->bitrate; - inst->vsi->config.pic_w = enc_prm->width; - inst->vsi->config.pic_h = enc_prm->height; - inst->vsi->config.buf_w = enc_prm->buf_width; - inst->vsi->config.buf_h = enc_prm->buf_height; - inst->vsi->config.gop_size = enc_prm->gop_size; - inst->vsi->config.framerate = enc_prm->frm_rate; - inst->vsi->config.intra_period = enc_prm->intra_period; - inst->vsi->config.profile = - h264_get_profile(inst, enc_prm->h264_profile); - inst->vsi->config.level = - h264_get_level(inst, enc_prm->h264_level); - inst->vsi->config.wfd = 0; + if (is_34bit) + h264_enc_set_vsi_34_configs(inst, enc_prm); + else + h264_enc_set_vsi_configs(inst, enc_prm); ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); if (ret) break; @@ -656,7 +790,7 @@ static int h264_enc_set_param(void *handle, h264_enc_free_work_buf(inst); inst->work_buf_allocated = false; } - ret = h264_enc_alloc_work_buf(inst); + ret = h264_enc_alloc_work_buf(inst, is_34bit); if (ret) break; inst->work_buf_allocated = true; diff --git a/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h index 587a2cf15b76..bb16d96a7f57 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h +++ b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h @@ -100,6 +100,30 @@ struct venc_ap_ipi_msg_enc_ext { uint32_t data[32]; }; +/** + * struct venc_ap_ipi_msg_enc_ext_34 - AP to SCP extended enc cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) + * @vpu_inst_addr: VPU encoder instance addr + * @bs_mode: bitstream mode for h264 + * @reserved: for struct padding + * @input_addr: input frame buffer 34 bit address + * @bs_addr: output bitstream buffer 34 bit address + * @bs_size: bitstream buffer size + * @data_item: number of items in the data array + * @data: data array to store the set parameters + */ +struct venc_ap_ipi_msg_enc_ext_34 { + u32 msg_id; + u32 vpu_inst_addr; + u32 bs_mode; + u32 reserved; + u64 input_addr[3]; + u64 bs_addr; + u32 bs_size; + u32 data_item; + u32 data[32]; +}; + /** * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) diff --git a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c index d3570c4c177d..09e7eaa25aab 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c @@ -222,10 +222,11 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, return 0; } -int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, - struct venc_frm_buf *frm_buf, - struct mtk_vcodec_mem *bs_buf, - struct venc_frame_info *frame_info) +static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu, + unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_frame_info *frame_info) { const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); size_t msg_size = is_ext ? @@ -267,6 +268,73 @@ int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, return -EINVAL; } + return 0; +} + +static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu, + unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_frame_info *frame_info) +{ + struct venc_ap_ipi_msg_enc_ext_34 out; + size_t msg_size = sizeof(struct venc_ap_ipi_msg_enc_ext_34); + + mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_ENCODE; + out.vpu_inst_addr = vpu->inst_addr; + out.bs_mode = bs_mode; + + if (frm_buf) { + if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) && + (frm_buf->fb_addr[1].dma_addr % 16 == 0) && + (frm_buf->fb_addr[2].dma_addr % 16 == 0)) { + out.input_addr[0] = frm_buf->fb_addr[0].dma_addr; + out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; + out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; + } else { + mtk_vcodec_err(vpu, "dma_addr not align to 16"); + return -EINVAL; + } + } + if (bs_buf) { + out.bs_addr = bs_buf->dma_addr; + out.bs_size = bs_buf->size; + } + if (frame_info) { + out.data_item = 3; + out.data[0] = frame_info->frm_count; + out.data[1] = frame_info->skip_frm_count; + out.data[2] = frame_info->frm_type; + } + if (vpu_enc_send_msg(vpu, &out, msg_size)) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", + bs_mode); + return -EINVAL; + } + + return 0; +} + +int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_frame_info *frame_info) +{ + int ret; + + if (MTK_ENC_IOVA_IS_34BIT(vpu->ctx)) + ret = vpu_enc_encode_34bits(vpu, bs_mode, + frm_buf, bs_buf, frame_info); + else + ret = vpu_enc_encode_32bits(vpu, bs_mode, + frm_buf, bs_buf, frame_info); + + if (ret) + return ret; + mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-", bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); -- cgit v1.3-14-g43fede From e9164d272fa5a59c10d7c09118b752d02ae8429b Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:27 +0200 Subject: media: dt-bindings: media: mediatek: vcodec: Add encoder dt-bindings for mt8188 Add encoder dt-bindings for mt8188. Signed-off-by: Irui Wang Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml b/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml index d36fcca04cbc..32aee09aea33 100644 --- a/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml +++ b/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml @@ -20,6 +20,7 @@ properties: - mediatek,mt8173-vcodec-enc-vp8 - mediatek,mt8173-vcodec-enc - mediatek,mt8183-vcodec-enc + - mediatek,mt8188-vcodec-enc - mediatek,mt8192-vcodec-enc - mediatek,mt8195-vcodec-enc -- cgit v1.3-14-g43fede From 53ed4873a256cabf70c6698c7bfffdbdc26a2a92 Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:28 +0200 Subject: media: mediatek: vcodec: Add mt8188 encoder driver Add mt8188's compatible "mediatek,mt8188-vcodec-enc". Add mt8188's device private data "mt8188_pdata". Signed-off-by: Irui Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index 95e8c29ccc65..ea667b867b56 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -406,6 +406,18 @@ static const struct mtk_vcodec_enc_pdata mt8183_pdata = { .core_id = VENC_SYS, }; +static const struct mtk_vcodec_enc_pdata mt8188_pdata = { + .uses_ext = true, + .capture_formats = mtk_video_formats_capture_h264, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 50000000, + .core_id = VENC_SYS, + .uses_34bit = true, +}; + static const struct mtk_vcodec_enc_pdata mt8192_pdata = { .uses_ext = true, .capture_formats = mtk_video_formats_capture_h264, @@ -434,6 +446,7 @@ static const struct of_device_id mtk_vcodec_enc_match[] = { {.compatible = "mediatek,mt8173-vcodec-enc-vp8", .data = &mt8173_vp8_pdata}, {.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata}, + {.compatible = "mediatek,mt8188-vcodec-enc", .data = &mt8188_pdata}, {.compatible = "mediatek,mt8192-vcodec-enc", .data = &mt8192_pdata}, {.compatible = "mediatek,mt8195-vcodec-enc", .data = &mt8195_pdata}, {}, -- cgit v1.3-14-g43fede From 223afdf9caa073114a25b79915781e09eadb23b7 Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:29 +0200 Subject: media: mediatek: vcodec: Remove encoder driver get IRQ resource The "platform_get_resource(pdev, IORESOURCE_IRQ, 0)" is no longer used after commit a1a2b7125e107("of/platform: Drop static setup of IRQ resource from DT core"), so just remove the function in encoder driver to avoid driver probe failed. Signed-off-by: Irui Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index ea667b867b56..6d8964fb4fa2 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -228,7 +228,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) { struct mtk_vcodec_dev *dev; struct video_device *vfd_enc; - struct resource *res; phandle rproc_phandle; enum mtk_vcodec_fw_type fw_type; int ret; @@ -272,13 +271,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_res; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get irq resource"); - ret = -ENOENT; - goto err_res; - } - dev->enc_irq = platform_get_irq(pdev, 0); irq_set_status_flags(dev->enc_irq, IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, dev->enc_irq, -- cgit v1.3-14-g43fede From cd61f3c6794bea2b717fe6083ca2ad189db75418 Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:30 +0200 Subject: media: mediatek: vcodec: Fix bitstream crop information error Usually, the real bitstream width and height will set to driver by vidioc_s_fmt, and vidioc_try_fmt() does align to get the buffer width and height, driver calculate the encoded bitstream crop information through them. The aligned resolution will be set as real resolution now if user didn't set crop info by V4L2_SEL_TGT_CROP, and the encoded bitstream may exist green line because of crop information error. Fixs: 'b6c57d313f5f8 ("media: mtk-vcodec: venc: remove redundant code")' Signed-off-by: Irui Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index 25e816863597..c310bb1dbbcf 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -503,13 +503,13 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, f->fmt.pix.pixelformat = fmt->fourcc; } - ret = vidioc_try_fmt_out(ctx, f, fmt); + q_data->visible_width = f->fmt.pix_mp.width; + q_data->visible_height = f->fmt.pix_mp.height; + q_data->fmt = fmt; + ret = vidioc_try_fmt_out(ctx, f, q_data->fmt); if (ret) return ret; - q_data->fmt = fmt; - q_data->visible_width = f->fmt.pix_mp.width; - q_data->visible_height = f->fmt.pix_mp.height; q_data->coded_width = f->fmt.pix_mp.width; q_data->coded_height = f->fmt.pix_mp.height; -- cgit v1.3-14-g43fede From 58037ad43f2d33746d0d7d16514de44d6e8f566e Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Wed, 20 Jul 2022 10:57:31 +0200 Subject: media: mediatek: vcodec: Use ctx vb2_queue mutex instead of device mutex There is only one device mutex to lock vb2_queue when running multi-instance encoding, it can be set by each encoder context. [hverkuil: fix q_mutex documentation in the header] Signed-off-by: Irui Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h | 3 +++ drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c | 6 +++--- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h index fe8ed6a456e0..9acab54fd650 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h @@ -278,6 +278,7 @@ struct vdec_pic_info { * @hw_id: hardware index used to identify different hardware. * * @msg_queue: msg queue used to store lat buffer information. + * @q_mutex: vb2_queue mutex. */ struct mtk_vcodec_ctx { enum mtk_instance_type type; @@ -324,6 +325,8 @@ struct mtk_vcodec_ctx { int hw_id; struct vdec_msg_queue msg_queue; + + struct mutex q_mutex; }; /* diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index c310bb1dbbcf..63e7fe958406 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -1300,7 +1300,7 @@ void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) { struct mtk_q_data *q_data; - ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->m2m_ctx->q_lock = &ctx->q_mutex; ctx->fh.m2m_ctx = ctx->m2m_ctx; ctx->fh.ctrl_handler = &ctx->ctrl_hdl; INIT_WORK(&ctx->encode_work, mtk_venc_worker); @@ -1435,7 +1435,7 @@ int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &mtk_venc_vb2_ops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; + src_vq->lock = &ctx->q_mutex; src_vq->dev = &ctx->dev->plat_dev->dev; ret = vb2_queue_init(src_vq); @@ -1449,7 +1449,7 @@ int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &mtk_venc_vb2_ops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->lock = &ctx->q_mutex; dst_vq->dev = &ctx->dev->plat_dev->dev; return vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index 6d8964fb4fa2..0abe1dac75b3 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -130,6 +130,7 @@ static int fops_vcodec_open(struct file *file) INIT_LIST_HEAD(&ctx->list); ctx->dev = dev; init_waitqueue_head(&ctx->queue[0]); + mutex_init(&ctx->q_mutex); ctx->type = MTK_INST_ENCODER; ret = mtk_vcodec_enc_ctrls_setup(ctx); -- cgit v1.3-14-g43fede From 8fcfa82556322ee80e83c2eb6c049396f26781a9 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Wed, 27 Jul 2022 04:37:19 +0200 Subject: media: dt-bindings: media: mediatek: vcodec: add decoder dt-bindings for mt8188 Add decoder document in dt-bindings yaml file for mt8188 platform. Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml b/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml index d4e2051beeb6..c4f20acdc1f8 100644 --- a/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml +++ b/Documentation/devicetree/bindings/media/mediatek,vcodec-subdev-decoder.yaml @@ -57,6 +57,7 @@ properties: enum: - mediatek,mt8192-vcodec-dec - mediatek,mt8186-vcodec-dec + - mediatek,mt8188-vcodec-dec - mediatek,mt8195-vcodec-dec reg: -- cgit v1.3-14-g43fede From b9b9db6a2d68b16b8473ae79a1aae255feaf1bca Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Wed, 27 Jul 2022 04:37:20 +0200 Subject: media: mediatek: vcodec: add decoder compatible to support mt8188 1: add mt8188's compatible name: mediatek,mt8188-vcodec-dec. 2: mt8188 is lat single core architecture, using mtk_lat_sig_core_pdata to initialize private data. 3: Getting mt8188's chip name according to decoder compatible name. Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c | 2 ++ drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c index 7d194a476713..641f533c417f 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c @@ -227,6 +227,8 @@ static int mtk_vcodec_dec_get_chip_name(void *priv) return 8195; else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) return 8186; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-dec")) + return 8188; else return 8173; } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c index e0b6ae9d6caa..174a6eec2f54 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c @@ -478,6 +478,10 @@ static const struct of_device_id mtk_vcodec_match[] = { .compatible = "mediatek,mt8195-vcodec-dec", .data = &mtk_lat_sig_core_pdata, }, + { + .compatible = "mediatek,mt8188-vcodec-dec", + .data = &mtk_lat_sig_core_pdata, + }, {}, }; -- cgit v1.3-14-g43fede From b813e39a6906908e2169574063cace9a03bd8ed6 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Wed, 27 Jul 2022 04:37:21 +0200 Subject: media: mediatek: vcodec: Add mt8188 encoder's chip name Getting mt8188's chip name according to encoder compatible name. Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index 63e7fe958406..16f1c3c034df 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -225,6 +225,8 @@ static int mtk_vcodec_enc_get_chip_name(void *priv) return 8192; else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-enc")) return 8195; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-enc")) + return 8188; else return 8173; } -- cgit v1.3-14-g43fede From e7bfdf0a854037e8c0597f1f44f72651869c424d Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Tue, 2 Aug 2022 06:42:42 +0200 Subject: media: mediatek: vcodec: Skip non CBR bitrate mode V4L2_MPEG_VIDEO_BITRATE_MODE_CBR is the only bitrate mode supported by the mediatek driver. The other bitrates must be skipped in QUERY_MENU. Fixes: d8e8aa866ed8 ("media: mediatek: vcodec: Report supported bitrate modes") Signed-off-by: Hirokazu Honda Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index 16f1c3c034df..d810a78dde51 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -1405,7 +1405,8 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) V4L2_MPEG_VIDEO_VP8_PROFILE_0, 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, - 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + ~(1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR), + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); if (handler->error) { -- cgit v1.3-14-g43fede From e21cde4016a6b8e4b8b7f56a9961554324cd33ed Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 19 Jul 2022 00:02:10 +0200 Subject: media: cedrus: Use vb2_find_buffer Use the newly introduced vb2_find_buffer API to get a vb2_buffer given a buffer timestamp. Cc: Maxime Ripard Cc: Paul Kocialkowski Cc: Jernej Skrabec Signed-off-by: Ezequiel Garcia Acked-by: Tomasz Figa Reviewed-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus.h | 24 +++++++------ drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 16 ++++----- drivers/staging/media/sunxi/cedrus/cedrus_h265.c | 18 +++++----- drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 28 +++++---------- drivers/staging/media/sunxi/cedrus/cedrus_vp8.c | 43 +++++------------------ 5 files changed, 47 insertions(+), 82 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 084193019350..93a2196006f7 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -237,19 +237,23 @@ 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, - int index, unsigned int plane) + struct vb2_buffer *buf, + unsigned int plane) { - struct vb2_buffer *buf = NULL; - struct vb2_queue *vq; - - if (index < 0) - return 0; + return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; +} - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (vq) - buf = vb2_get_buffer(vq, index); +static inline void cedrus_write_ref_buf_addr(struct cedrus_ctx *ctx, + struct vb2_queue *q, + u64 timestamp, + u32 luma_reg, + u32 chroma_reg) +{ + struct cedrus_dev *dev = ctx->dev; + struct vb2_buffer *buf = vb2_find_buffer(q, timestamp); - return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; + cedrus_write(dev, luma_reg, cedrus_dst_buf_addr(ctx, buf, 0)); + cedrus_write(dev, chroma_reg, cedrus_dst_buf_addr(ctx, buf, 1)); } static inline struct cedrus_buffer * diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index c345e67ba9bc..a8b236cd3800 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -111,16 +111,16 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, for (i = 0; i < ARRAY_SIZE(decode->dpb); i++) { const struct v4l2_h264_dpb_entry *dpb = &decode->dpb[i]; struct cedrus_buffer *cedrus_buf; - int buf_idx; + struct vb2_buffer *buf; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) continue; - buf_idx = vb2_find_timestamp(cap_q, dpb->reference_ts, 0); - if (buf_idx < 0) + buf = vb2_find_buffer(cap_q, dpb->reference_ts); + if (!buf) continue; - cedrus_buf = vb2_to_cedrus_buffer(cap_q->bufs[buf_idx]); + cedrus_buf = vb2_to_cedrus_buffer(buf); position = cedrus_buf->codec.h264.position; used_dpbs |= BIT(position); @@ -186,7 +186,7 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, const struct v4l2_h264_dpb_entry *dpb; const struct cedrus_buffer *cedrus_buf; unsigned int position; - int buf_idx; + struct vb2_buffer *buf; u8 dpb_idx; dpb_idx = ref_list[i].index; @@ -195,11 +195,11 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) continue; - buf_idx = vb2_find_timestamp(cap_q, dpb->reference_ts, 0); - if (buf_idx < 0) + buf = vb2_find_buffer(cap_q, dpb->reference_ts); + if (!buf) continue; - cedrus_buf = vb2_to_cedrus_buffer(cap_q->bufs[buf_idx]); + cedrus_buf = vb2_to_cedrus_buffer(buf); position = cedrus_buf->codec.h264.position; sram_array[i] |= position << 1; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index 687f87598f78..f703c585d91c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -102,14 +102,14 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, unsigned int index, bool field_pic, u32 pic_order_cnt[], - int buffer_index) + struct vb2_buffer *buf) { struct cedrus_dev *dev = ctx->dev; - dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 0); - dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 1); + dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0); + dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1); dma_addr_t mv_col_buf_addr[2] = { - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, 0), - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, + cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, 0), + cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, field_pic ? 1 : 0) }; u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + @@ -141,18 +141,18 @@ static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx, unsigned int i; for (i = 0; i < num_active_dpb_entries; i++) { - int buffer_index = vb2_find_timestamp(vq, dpb[i].timestamp, 0); + struct vb2_buffer *buf = vb2_find_buffer(vq, dpb[i].timestamp); u32 pic_order_cnt[2] = { dpb[i].pic_order_cnt_val, dpb[i].pic_order_cnt_val }; - if (buffer_index < 0) + if (!buf) continue; cedrus_h265_frame_info_write_single(ctx, i, dpb[i].field_pic, pic_order_cnt, - buffer_index); + buf); } } @@ -751,7 +751,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_h265_frame_info_write_single(ctx, output_pic_list_index, slice_params->pic_struct != 0, pic_order_cnt, - run->dst->vb2_buf.index); + &run->dst->vb2_buf); cedrus_write(dev, VE_DEC_H265_OUTPUT_FRAME_IDX, output_pic_list_index); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 4cfc4a3c8a7f..c1128d2cd555 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -54,13 +54,9 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) const struct v4l2_ctrl_mpeg2_picture *pic; const struct v4l2_ctrl_mpeg2_quantisation *quantisation; dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr; - 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 *vq; const u8 *matrix; - int forward_idx; - int backward_idx; unsigned int i; u32 reg; @@ -123,27 +119,19 @@ static int 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. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - forward_idx = vb2_find_timestamp(vq, pic->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(vq, pic->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); + cedrus_write_ref_buf_addr(ctx, vq, pic->forward_ref_ts, + VE_DEC_MPEG_FWD_REF_LUMA_ADDR, + VE_DEC_MPEG_FWD_REF_CHROMA_ADDR); + cedrus_write_ref_buf_addr(ctx, vq, pic->backward_ref_ts, + VE_DEC_MPEG_BWD_REF_LUMA_ADDR, + VE_DEC_MPEG_BWD_REF_CHROMA_ADDR); /* Destination luma and chroma buffers. */ - dst_luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); - dst_chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + dst_luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0); + dst_chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1); cedrus_write(dev, VE_DEC_MPEG_REC_LUMA, dst_luma_addr); cedrus_write(dev, VE_DEC_MPEG_REC_CHROMA, dst_chroma_addr); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c index 3f750d1795b6..f7714baae37d 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c @@ -660,7 +660,6 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t luma_addr, chroma_addr; dma_addr_t src_buf_addr; int header_size; - int qindex; u32 reg; cedrus_engine_enable(ctx, CEDRUS_CODEC_VP8); @@ -804,43 +803,17 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) reg |= VE_VP8_LF_DELTA0(slice->lf.mb_mode_delta[0]); cedrus_write(dev, VE_VP8_MODE_LF_DELTA, reg); - luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1); cedrus_write(dev, VE_VP8_REC_LUMA, luma_addr); cedrus_write(dev, VE_VP8_REC_CHROMA, chroma_addr); - qindex = vb2_find_timestamp(cap_q, slice->last_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_FWD_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_FWD_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_FWD_LUMA, 0); - cedrus_write(dev, VE_VP8_FWD_CHROMA, 0); - } - - qindex = vb2_find_timestamp(cap_q, slice->golden_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_BWD_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_BWD_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_BWD_LUMA, 0); - cedrus_write(dev, VE_VP8_BWD_CHROMA, 0); - } - - qindex = vb2_find_timestamp(cap_q, slice->alt_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_ALT_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_ALT_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_ALT_LUMA, 0); - cedrus_write(dev, VE_VP8_ALT_CHROMA, 0); - } + cedrus_write_ref_buf_addr(ctx, cap_q, slice->last_frame_ts, + VE_VP8_FWD_LUMA, VE_VP8_FWD_CHROMA); + cedrus_write_ref_buf_addr(ctx, cap_q, slice->golden_frame_ts, + VE_VP8_BWD_LUMA, VE_VP8_BWD_CHROMA); + cedrus_write_ref_buf_addr(ctx, cap_q, slice->alt_frame_ts, + VE_VP8_ALT_LUMA, VE_VP8_ALT_CHROMA); cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8 | VE_H264_CTRL_DECODE_ERR_INT | -- cgit v1.3-14-g43fede From 2801f6f30f11dfe790a7f2cd63e004e10057a952 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 19 Jul 2022 00:02:11 +0200 Subject: media: videobuf2: Remove vb2_find_timestamp() Now that we've transitioned all users to vb2_find_buffer API, remove the unused vb2_find_timestamp(). Signed-off-by: Ezequiel Garcia Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 13 ------------- include/media/videobuf2-v4l2.h | 16 ---------------- 2 files changed, 29 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 2ecd4483e139..1f5d235a8441 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -625,19 +625,6 @@ 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++) - if (q->bufs[i]->copied_timestamp && - q->bufs[i]->timestamp == timestamp) - return i; - return -1; -} -EXPORT_SYMBOL_GPL(vb2_find_timestamp); - struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp) { unsigned int i; diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 76e405c0b003..5a845887850b 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -62,22 +62,6 @@ 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. - * @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); - /** * vb2_find_buffer() - Find a buffer with given timestamp * -- cgit v1.3-14-g43fede From fe8b81fde69acfcbb5af9e85328e5b9549999fdb Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 18 Aug 2022 22:33:06 +0200 Subject: media: cedrus: Fix watchdog race condition The watchdog needs to be scheduled before we trigger the decode operation, otherwise there is a risk that the decoder IRQ will be called before we have schedule the watchdog. As a side effect, the watchdog would never be cancelled and its function would be called at an inappropriate time. This was observed while running Fluster with GStreamer as a backend. Some programming error would cause the decoder IRQ to be call very quickly after the trigger. Later calls into the driver would deadlock due to the unbalanced state. Cc: stable@vger.kernel.org Fixes: 7c38a551bda1 ("media: cedrus: Add watchdog for job completion") Signed-off-by: Nicolas Dufresne Reviewed-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_dec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 3b6aa78a2985..e7f7602a5ab4 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -106,11 +106,11 @@ void cedrus_device_run(void *priv) /* Trigger decoding if setup went well, bail out otherwise. */ if (!error) { - dev->dec_ops[ctx->current_codec]->trigger(ctx); - /* Start the watchdog timer. */ schedule_delayed_work(&dev->watchdog_work, msecs_to_jiffies(2000)); + + dev->dec_ops[ctx->current_codec]->trigger(ctx); } else { v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, -- cgit v1.3-14-g43fede From 708938f8495147fe2e77a9a3e1015d8e6899323e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 18 Aug 2022 22:33:07 +0200 Subject: media: cedrus: Set the platform driver data earlier The cedrus_hw_resume() crashes with NULL deference on driver probe if runtime PM is disabled because it uses platform data that hasn't been set up yet. Fix this by setting the platform data earlier during probe. Cc: stable@vger.kernel.org Fixes: 50e761516f2b (media: platform: Add Cedrus VPU decoder driver) Signed-off-by: Dmitry Osipenko Signed-off-by: Nicolas Dufresne Reviewed-by: Samuel Holland Acked-by: Paul Kocialkowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 960a0130cd62..55c54dfdc585 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -448,6 +448,8 @@ static int cedrus_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; + platform_set_drvdata(pdev, dev); + dev->vfd = cedrus_video_device; dev->dev = &pdev->dev; dev->pdev = pdev; @@ -521,8 +523,6 @@ static int cedrus_probe(struct platform_device *pdev) goto err_m2m_mc; } - platform_set_drvdata(pdev, dev); - return 0; err_m2m_mc: -- cgit v1.3-14-g43fede From 91db7a3fc7fe670cf1770a398a43bb4a1f776bf1 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 18 Aug 2022 22:33:08 +0200 Subject: media: cedrus: Fix endless loop in cedrus_h265_skip_bits() The busy status bit may never de-assert if number of programmed skip bits is incorrect, resulting in a kernel hang because the bit is polled endlessly in the code. Fix it by adding timeout for the bit-polling. This problem is reproducible by setting the data_bit_offset field of the HEVC slice params to a wrong value by userspace. Cc: stable@vger.kernel.org Fixes: 7678c5462680 (media: cedrus: Fix decoding for some HEVC videos) Reported-by: Nicolas Dufresne Signed-off-by: Dmitry Osipenko Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/sunxi/cedrus/cedrus_h265.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index f703c585d91c..4952fc17f3e6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -234,8 +234,9 @@ static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_FLUSH_BITS | VE_DEC_H265_TRIGGER_TYPE_N_BITS(tmp)); - while (cedrus_read(dev, VE_DEC_H265_STATUS) & VE_DEC_H265_STATUS_VLD_BUSY) - udelay(1); + + if (cedrus_wait_for(dev, VE_DEC_H265_STATUS, VE_DEC_H265_STATUS_VLD_BUSY)) + dev_err_ratelimited(dev->dev, "timed out waiting to skip bits\n"); count += tmp; } -- cgit v1.3-14-g43fede From f7fd6c318c8a5d06bf3fe611f30763d62eaaf7f0 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 15 Jul 2022 09:15:49 +0200 Subject: media: amphion: insert picture startcode after seek for vc1g format For format vc1, the amphion vpu requires driver to help insert some custom startcode before sequence and frame. the startcode is different for vc1l and vc1g format. But the sequence startcode is only needed at the beginning, and it's not expected after seek. driver need to treat the codec header and the first frame after seek as a normal frame, and insert picture startcode for it. In previous patch, I just fix it for vc1l format, and should fix the similar issue for vc1g too. Fixes: e670f5d672ef (media: amphion: only insert the first sequence startcode for vc1l format) Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/amphion/vpu_malone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index f4a488bf9880..51e0702f9ae1 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -1293,7 +1293,7 @@ static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode) vbuf = to_vb2_v4l2_buffer(scode->vb); data = vb2_plane_vaddr(scode->vb, 0); - if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf)) + if (scode->inst->total_input_count == 0 || vpu_vb_is_codecconfig(vbuf)) return 0; if (MALONE_VC1_CONTAIN_NAL(*data)) return 0; -- cgit v1.3-14-g43fede From 996f4e89fabe44ab9ac0aabb0697aeecbe717eca Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 15 Jul 2022 09:38:00 +0200 Subject: media: amphion: adjust the encoder's value range of gop size adjust the value range of gop size from [0, 65535] to [1, 8000]. when the gop size is set to a too large value, it may affect the encoded picture quality. so constrain it to a reasonable range. Fixes: 0401e659c1f92 ("media: amphion: add v4l2 m2m vpu encoder stateful driver") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/amphion/venc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 461524dd1e44..37212f087fdd 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -644,7 +644,7 @@ static int venc_ctrl_init(struct vpu_inst *inst) BITRATE_DEFAULT_PEAK); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 30); + V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 8000, 1, 30); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0); -- cgit v1.3-14-g43fede From 61c2698ee60630c6a7d2e99850fa81ff6450270a Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Tue, 26 Jul 2022 05:02:29 +0200 Subject: media: amphion: don't change the colorspace reported by decoder. decoder will report the colorspace information which is parsed from the sequence header, if they are unspecified, just let application to determine it, don't change it in driver. Fixes: 6de8d628df6ef ("media: amphion: add v4l2 m2m vpu decoder stateful driver") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/amphion/vdec.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 9e64041cc1c1..feb75dc204de 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -808,14 +808,6 @@ static void vdec_init_fmt(struct vpu_inst *inst) inst->cap_format.field = V4L2_FIELD_NONE; else inst->cap_format.field = V4L2_FIELD_SEQ_TB; - if (vdec->codec_info.color_primaries == V4L2_COLORSPACE_DEFAULT) - vdec->codec_info.color_primaries = V4L2_COLORSPACE_REC709; - if (vdec->codec_info.transfer_chars == V4L2_XFER_FUNC_DEFAULT) - vdec->codec_info.transfer_chars = V4L2_XFER_FUNC_709; - if (vdec->codec_info.matrix_coeffs == V4L2_YCBCR_ENC_DEFAULT) - vdec->codec_info.matrix_coeffs = V4L2_YCBCR_ENC_709; - if (vdec->codec_info.full_range == V4L2_QUANTIZATION_DEFAULT) - vdec->codec_info.full_range = V4L2_QUANTIZATION_LIM_RANGE; } static void vdec_init_crop(struct vpu_inst *inst) @@ -1555,6 +1547,14 @@ static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i vdec->codec_info.frame_rate.numerator, vdec->codec_info.frame_rate.denominator); break; + case 9: + num = scnprintf(str, size, "colorspace: %d, %d, %d, %d (%d)\n", + vdec->codec_info.color_primaries, + vdec->codec_info.transfer_chars, + vdec->codec_info.matrix_coeffs, + vdec->codec_info.full_range, + vdec->codec_info.vui_present); + break; default: break; } -- cgit v1.3-14-g43fede From c65c3f3a2cbf21ed429d9b9c725bdb5dc6abf4cf Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Tue, 16 Aug 2022 10:58:19 +0200 Subject: media: platform: fix some double free in meson-ge2d and mtk-jpeg and s5p-mfc video_unregister_device will release device internally. There is no need to call video_device_release after video_unregister_device. Signed-off-by: Hangyu Hua Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/amlogic/meson-ge2d/ge2d.c | 1 - drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 1 - drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c index 5e7b319f300d..142d421a8d76 100644 --- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c @@ -1030,7 +1030,6 @@ static int ge2d_remove(struct platform_device *pdev) video_unregister_device(ge2d->vfd); v4l2_m2m_release(ge2d->m2m_dev); - video_device_release(ge2d->vfd); v4l2_device_unregister(&ge2d->v4l2_dev); clk_disable_unprepare(ge2d->clk); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 87685a62a5c2..3071b61946c3 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1414,7 +1414,6 @@ static int mtk_jpeg_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); video_unregister_device(jpeg->vdev); - video_device_release(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 219fc0235b69..fca5c6405eec 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -1399,6 +1399,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) /* Deinit MFC if probe had failed */ err_enc_reg: video_unregister_device(dev->vfd_dec); + dev->vfd_dec = NULL; err_dec_reg: video_device_release(dev->vfd_enc); err_enc_alloc: @@ -1444,8 +1445,6 @@ static int s5p_mfc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); - video_device_release(dev->vfd_enc); - video_device_release(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); s5p_mfc_unconfigure_dma_memory(dev); -- cgit v1.3-14-g43fede From 0202a665bf17fbe98fed954944aabbcb4f14a4cc Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Thu, 18 Aug 2022 05:18:21 +0200 Subject: media: amphion: fix a bug that vpu core may not resume after suspend driver will enable the vpu core when request the first instance on the core. one vpu core can only support 8 streaming instances in the same time, the instance won't be added to core's list before streamon. so the actual instance count may be greater then the number in the core's list. in pm resume callback, driver will resume the core immediately if core's list is not empty. but this check is not accurate, if suspend during one instance is requested, but not streamon, then after suspend, the core won't be resume, and led to instance failure. use the request_count instead of the core's list to check whether is the core needed to resume immediately after suspend. And it can make the pm suspend and resume callback more clear. Fixes: 9f599f351e86 ("media: amphion: add vpu core driver") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/amphion/vpu.h | 1 - drivers/media/platform/amphion/vpu_core.c | 84 ++++++++++++++++--------------- drivers/media/platform/amphion/vpu_core.h | 1 + drivers/media/platform/amphion/vpu_dbg.c | 9 +++- 4 files changed, 51 insertions(+), 44 deletions(-) diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index f914de6ed81e..beac0309ca8d 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -119,7 +119,6 @@ struct vpu_mbox { enum vpu_core_state { VPU_CORE_DEINIT = 0, VPU_CORE_ACTIVE, - VPU_CORE_SNAPSHOT, VPU_CORE_HANG }; diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 73faa50d2865..f9ec1753f7c8 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -89,7 +89,7 @@ static int vpu_core_boot_done(struct vpu_core *core) core->supported_instance_count = min(core->supported_instance_count, count); } core->fw_version = fw_version; - core->state = VPU_CORE_ACTIVE; + vpu_core_set_state(core, VPU_CORE_ACTIVE); return 0; } @@ -172,10 +172,26 @@ int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf) return __vpu_alloc_dma(core->dev, buf); } -static void vpu_core_check_hang(struct vpu_core *core) +void vpu_core_set_state(struct vpu_core *core, enum vpu_core_state state) { - if (core->hang_mask) - core->state = VPU_CORE_HANG; + if (state != core->state) + vpu_trace(core->dev, "vpu core state change from %d to %d\n", core->state, state); + core->state = state; + if (core->state == VPU_CORE_DEINIT) + core->hang_mask = 0; +} + +static void vpu_core_update_state(struct vpu_core *core) +{ + if (!vpu_iface_get_power_state(core)) { + if (core->request_count) + vpu_core_set_state(core, VPU_CORE_HANG); + else + vpu_core_set_state(core, VPU_CORE_DEINIT); + + } else if (core->state == VPU_CORE_ACTIVE && core->hang_mask) { + vpu_core_set_state(core, VPU_CORE_HANG); + } } static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 type) @@ -188,11 +204,13 @@ static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 ty dev_dbg(c->dev, "instance_mask = 0x%lx, state = %d\n", c->instance_mask, c->state); if (c->type != type) continue; + mutex_lock(&c->lock); + vpu_core_update_state(c); + mutex_unlock(&c->lock); if (c->state == VPU_CORE_DEINIT) { core = c; break; } - vpu_core_check_hang(c); if (c->state != VPU_CORE_ACTIVE) continue; if (c->request_count < request_count) { @@ -409,6 +427,12 @@ int vpu_inst_register(struct vpu_inst *inst) } mutex_lock(&core->lock); + if (core->state != VPU_CORE_ACTIVE) { + dev_err(core->dev, "vpu core is not active, state = %d\n", core->state); + ret = -EINVAL; + goto exit; + } + if (inst->id >= 0 && inst->id < core->supported_instance_count) goto exit; @@ -450,7 +474,7 @@ int vpu_inst_unregister(struct vpu_inst *inst) vpu_core_release_instance(core, inst->id); inst->id = VPU_INST_NULL_ID; } - vpu_core_check_hang(core); + vpu_core_update_state(core); if (core->state == VPU_CORE_HANG && !core->instance_mask) { int err; @@ -459,7 +483,7 @@ int vpu_inst_unregister(struct vpu_inst *inst) err = vpu_core_sw_reset(core); mutex_lock(&core->lock); if (!err) { - core->state = VPU_CORE_ACTIVE; + vpu_core_set_state(core, VPU_CORE_ACTIVE); core->hang_mask = 0; } } @@ -609,7 +633,7 @@ static int vpu_core_probe(struct platform_device *pdev) mutex_init(&core->cmd_lock); init_completion(&core->cmp); init_waitqueue_head(&core->ack_wq); - core->state = VPU_CORE_DEINIT; + vpu_core_set_state(core, VPU_CORE_DEINIT); core->res = of_device_get_match_data(dev); if (!core->res) @@ -758,33 +782,18 @@ static int __maybe_unused vpu_core_resume(struct device *dev) mutex_lock(&core->lock); pm_runtime_resume_and_get(dev); vpu_core_get_vpu(core); - if (core->state != VPU_CORE_SNAPSHOT) - goto exit; - if (!vpu_iface_get_power_state(core)) { - if (!list_empty(&core->instances)) { + if (core->request_count) { + if (!vpu_iface_get_power_state(core)) ret = vpu_core_boot(core, false); - if (ret) { - dev_err(core->dev, "%s boot fail\n", __func__); - core->state = VPU_CORE_DEINIT; - goto exit; - } - } else { - core->state = VPU_CORE_DEINIT; - } - } else { - if (!list_empty(&core->instances)) { + else ret = vpu_core_sw_reset(core); - if (ret) { - dev_err(core->dev, "%s sw_reset fail\n", __func__); - core->state = VPU_CORE_HANG; - goto exit; - } + if (ret) { + dev_err(core->dev, "resume fail\n"); + vpu_core_set_state(core, VPU_CORE_HANG); } - core->state = VPU_CORE_ACTIVE; } - -exit: + vpu_core_update_state(core); pm_runtime_put_sync(dev); mutex_unlock(&core->lock); @@ -798,18 +807,11 @@ static int __maybe_unused vpu_core_suspend(struct device *dev) int ret = 0; mutex_lock(&core->lock); - if (core->state == VPU_CORE_ACTIVE) { - if (!list_empty(&core->instances)) { - ret = vpu_core_snapshot(core); - if (ret) { - mutex_unlock(&core->lock); - return ret; - } - } - - core->state = VPU_CORE_SNAPSHOT; - } + if (core->request_count) + ret = vpu_core_snapshot(core); mutex_unlock(&core->lock); + if (ret) + return ret; vpu_core_cancel_work(core); diff --git a/drivers/media/platform/amphion/vpu_core.h b/drivers/media/platform/amphion/vpu_core.h index 00a662997da4..65b562642603 100644 --- a/drivers/media/platform/amphion/vpu_core.h +++ b/drivers/media/platform/amphion/vpu_core.h @@ -11,5 +11,6 @@ u32 csr_readl(struct vpu_core *core, u32 reg); int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf); void vpu_free_dma(struct vpu_buffer *buf); struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index); +void vpu_core_set_state(struct vpu_core *core, enum vpu_core_state state); #endif diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index f72c8a506b22..260f1c4b8f8d 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -15,6 +15,7 @@ #include #include "vpu.h" #include "vpu_defs.h" +#include "vpu_core.h" #include "vpu_helpers.h" #include "vpu_cmds.h" #include "vpu_rpc.h" @@ -233,6 +234,10 @@ static int vpu_dbg_core(struct seq_file *s, void *data) if (seq_write(s, str, num)) return 0; + num = scnprintf(str, sizeof(str), "power %s\n", + vpu_iface_get_power_state(core) ? "on" : "off"); + if (seq_write(s, str, num)) + return 0; num = scnprintf(str, sizeof(str), "state = %d\n", core->state); if (seq_write(s, str, num)) return 0; @@ -346,10 +351,10 @@ static ssize_t vpu_dbg_core_write(struct file *file, pm_runtime_resume_and_get(core->dev); mutex_lock(&core->lock); - if (core->state != VPU_CORE_DEINIT && !core->instance_mask) { + if (vpu_iface_get_power_state(core) && !core->request_count) { dev_info(core->dev, "reset\n"); if (!vpu_core_sw_reset(core)) { - core->state = VPU_CORE_ACTIVE; + vpu_core_set_state(core, VPU_CORE_ACTIVE); core->hang_mask = 0; } } -- cgit v1.3-14-g43fede From 4029372233e13e281f8c387f279f9f064ced3810 Mon Sep 17 00:00:00 2001 From: Xu Qiang Date: Thu, 18 Aug 2022 08:57:53 +0200 Subject: media: meson: vdec: add missing clk_disable_unprepare on error in vdec_hevc_start() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the missing clk_disable_unprepare() before return from vdec_hevc_start() in the error handling case. Fixes: 823a7300340e (“media: meson: vdec: add common HEVC decoder support”) Signed-off-by: Xu Qiang Reviewed-by: Neil Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/meson/vdec/vdec_hevc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c index 9530e580e57a..afced435c907 100644 --- a/drivers/staging/media/meson/vdec/vdec_hevc.c +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c @@ -167,8 +167,12 @@ static int vdec_hevc_start(struct amvdec_session *sess) clk_set_rate(core->vdec_hevc_clk, 666666666); ret = clk_prepare_enable(core->vdec_hevc_clk); - if (ret) + if (ret) { + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) + clk_disable_unprepare(core->vdec_hevcf_clk); return ret; + } if (core->platform->revision == VDEC_REVISION_SM1) regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, -- cgit v1.3-14-g43fede From 17b5179ef6cd6430945c1da4174ceaf43c93cf1e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 28 Aug 2022 10:13:30 +0200 Subject: media: dt-bindings: media: renesas,vsp1: Document RZ/G2L VSPD bindings Document VSPD found in RZ/G2L SoC. VSPD block is similar to VSP2-D found on R-Car SoC's, but it does not have a version register and it has 3 clocks compared to 1 clock on vsp1 and vsp2. This patch introduces a new compatible 'renesas,r9a07g044-vsp2' to handle these differences. Signed-off-by: Biju Das Reviewed-by: Lad Prabhakar Reviewed-by: Krzysztof Kozlowski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/renesas,vsp1.yaml | 53 ++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 990e9c1dbc43..7a8f32473852 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -17,6 +17,7 @@ description: properties: compatible: enum: + - renesas,r9a07g044-vsp2 # RZ/G2L - renesas,vsp1 # R-Car Gen2 and RZ/G1 - renesas,vsp2 # R-Car Gen3 and RZ/G2 @@ -26,8 +27,8 @@ properties: interrupts: maxItems: 1 - clocks: - maxItems: 1 + clocks: true + clock-names: true power-domains: maxItems: 1 @@ -50,17 +51,43 @@ required: additionalProperties: false -if: - properties: - compatible: - items: - - const: renesas,vsp1 -then: - properties: - renesas,fcp: false -else: - required: - - renesas,fcp +allOf: + - if: + properties: + compatible: + contains: + const: renesas,vsp1 + then: + properties: + renesas,fcp: false + else: + required: + - renesas,fcp + + - if: + properties: + compatible: + contains: + const: renesas,r9a07g044-vsp2 + then: + properties: + clocks: + items: + - description: Main clock + - description: Register access clock + - description: Video clock + clock-names: + items: + - const: aclk + - const: pclk + - const: vclk + required: + - clock-names + else: + properties: + clocks: + maxItems: 1 + clock-names: false examples: # R8A7790 (R-Car H2) VSP1-S -- cgit v1.3-14-g43fede From 24c52aa35a00284c66eb3bfa9ff811d440fe6c14 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 28 Aug 2022 10:13:31 +0200 Subject: media: renesas: vsp1: Add support to deassert/assert reset line As the resets DT property is mandatory, and is present in all .dtsi in mainline, add support to perform deassert/assert using reference counted reset handle. Signed-off-by: Biju Das Reviewed-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Reviewed-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas/vsp1/vsp1.h | 2 ++ drivers/media/platform/renesas/vsp1/vsp1_drv.c | 29 ++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index 37cf33c7e6ca..baf898d577ec 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -22,6 +22,7 @@ struct clk; struct device; struct rcar_fcp_device; +struct reset_control; struct vsp1_drm; struct vsp1_entity; @@ -79,6 +80,7 @@ struct vsp1_device { void __iomem *mmio; struct rcar_fcp_device *fcp; struct device *bus_master; + struct reset_control *rstc; struct vsp1_brx *brs; struct vsp1_brx *bru; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 1f73c48eb738..ef4bcf860923 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -622,6 +623,7 @@ static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev) struct vsp1_device *vsp1 = dev_get_drvdata(dev); rcar_fcp_disable(vsp1->fcp); + reset_control_assert(vsp1->rstc); return 0; } @@ -631,13 +633,31 @@ static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev) struct vsp1_device *vsp1 = dev_get_drvdata(dev); int ret; + ret = reset_control_deassert(vsp1->rstc); + if (ret < 0) + return ret; + if (vsp1->info) { + /* + * On R-Car Gen2 and RZ/G1, vsp1 register access after deassert + * can cause lock-up. It is a special case and needs some delay + * to avoid this lock-up. + */ + if (vsp1->info->gen == 2) + udelay(1); + ret = vsp1_device_init(vsp1); if (ret < 0) - return ret; + goto done; } - return rcar_fcp_enable(vsp1->fcp); + ret = rcar_fcp_enable(vsp1->fcp); + +done: + if (ret < 0) + reset_control_assert(vsp1->rstc); + + return ret; } static const struct dev_pm_ops vsp1_pm_ops = { @@ -825,6 +845,11 @@ static int vsp1_probe(struct platform_device *pdev) if (irq < 0) return irq; + vsp1->rstc = devm_reset_control_get_shared(&pdev->dev, NULL); + if (IS_ERR(vsp1->rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(vsp1->rstc), + "failed to get reset control\n"); + /* FCP (optional). */ fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); if (fcp_node) { -- cgit v1.3-14-g43fede From 9c63902745020ca415806064ca8694b983ea436e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 28 Aug 2022 10:13:32 +0200 Subject: media: renesas: vsp1: Add support for VSP software version The VSPD block on RZ/G2L SoCs does not have a version register. This patch adds support for adding VSP software version based on device match. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas/vsp1/vsp1.h | 1 + drivers/media/platform/renesas/vsp1/vsp1_drv.c | 43 +++++++++++++++++-------- drivers/media/platform/renesas/vsp1/vsp1_regs.h | 2 ++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index baf898d577ec..ff4435705abb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -67,6 +67,7 @@ struct vsp1_device_info { unsigned int uif_count; unsigned int wpf_count; unsigned int num_bru_inputs; + u8 soc; bool uapi; }; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index ef4bcf860923..fd75788a5a36 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -818,11 +818,39 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, }; +static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) +{ + const struct vsp1_device_info *info; + unsigned int i; + + /* + * Try the info stored in match data first for devices that don't have + * a version register. + */ + info = of_device_get_match_data(vsp1->dev); + if (info) { + vsp1->version = VI6_IP_VERSION_VSP_SW | info->version | info->soc; + return info; + } + + vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); + + for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { + info = &vsp1_device_infos[i]; + + if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == info->version) + return info; + } + + dev_err(vsp1->dev, "unsupported IP version 0x%08x\n", vsp1->version); + + return NULL; +} + static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; struct device_node *fcp_node; - unsigned int i; int ret; int irq; @@ -878,19 +906,8 @@ static int vsp1_probe(struct platform_device *pdev) if (ret < 0) goto done; - vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); - - for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { - if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == - vsp1_device_infos[i].version) { - vsp1->info = &vsp1_device_infos[i]; - break; - } - } - + vsp1->info = vsp1_lookup_info(vsp1); if (!vsp1->info) { - dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", - vsp1->version); vsp1_device_put(vsp1); ret = -ENXIO; goto done; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index fae7286eb01e..4286d13eca32 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -781,6 +781,8 @@ #define VI6_IP_VERSION_SOC_E3 (0x04 << 0) #define VI6_IP_VERSION_SOC_V3U (0x05 << 0) +#define VI6_IP_VERSION_VSP_SW (0xfffe << 16) /* SW VSP version */ + /* ----------------------------------------------------------------------------- * RPF CLUT Registers */ -- cgit v1.3-14-g43fede From 4d728fd4c60e0f367321843290a618caf86b95cd Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 28 Aug 2022 10:13:33 +0200 Subject: media: renesas: vsp1: Add VSP1_HAS_NON_ZERO_LBA feature bit As per HW manual V3M and RZ/G2L SoCs has nonzero LIF buffer attributes. So, introduce a feature bit for handling the same. This patch also adds separate device info structure for V3M and V3H SoCs, as both these SoCs share the same model ID, but V3H does not have VSP1_HAS_NON_ZERO_LBA feature bit. Signed-off-by: Biju Das Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas/vsp1/vsp1.h | 1 + drivers/media/platform/renesas/vsp1/vsp1_drv.c | 18 +++++++++++++++++- drivers/media/platform/renesas/vsp1/vsp1_lif.c | 3 +-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index ff4435705abb..2f6f0c6ae555 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -55,6 +55,7 @@ struct vsp1_uif; #define VSP1_HAS_HGT BIT(8) #define VSP1_HAS_BRS BIT(9) #define VSP1_HAS_EXT_DL BIT(10) +#define VSP1_HAS_NON_ZERO_LBA BIT(11) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index fd75788a5a36..8ab5b2b37524 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -788,6 +788,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, { .version = VI6_IP_VERSION_MODEL_VSPD_V3, .model = "VSP2-D", + .soc = VI6_IP_VERSION_SOC_V3H, .gen = 3, .features = VSP1_HAS_BRS | VSP1_HAS_BRU, .lif_count = 1, @@ -795,6 +796,17 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uif_count = 1, .wpf_count = 1, .num_bru_inputs = 5, + }, { + .version = VI6_IP_VERSION_MODEL_VSPD_V3, + .model = "VSP2-D", + .soc = VI6_IP_VERSION_SOC_V3M, + .gen = 3, + .features = VSP1_HAS_BRS | VSP1_HAS_BRU | VSP1_HAS_NON_ZERO_LBA, + .lif_count = 1, + .rpf_count = 5, + .uif_count = 1, + .wpf_count = 1, + .num_bru_inputs = 5, }, { .version = VI6_IP_VERSION_MODEL_VSPDL_GEN3, .model = "VSP2-DL", @@ -822,6 +834,8 @@ static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) { const struct vsp1_device_info *info; unsigned int i; + u32 model; + u32 soc; /* * Try the info stored in match data first for devices that don't have @@ -834,11 +848,13 @@ static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) } vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); + model = vsp1->version & VI6_IP_VERSION_MODEL_MASK; + soc = vsp1->version & VI6_IP_VERSION_SOC_MASK; for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { info = &vsp1_device_infos[i]; - if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == info->version) + if (model == info->version && (!info->soc || soc == info->soc)) return info; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 6a6857ac9327..9adb892edcdc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -135,8 +135,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, * may appear on the output). The value required by the manual is not * explained but is likely a buffer size or threshold. */ - if ((entity->vsp1->version & VI6_IP_VERSION_MASK) == - (VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M)) + if (vsp1_feature(entity->vsp1, VSP1_HAS_NON_ZERO_LBA)) vsp1_lif_write(lif, dlb, VI6_LIF_LBA, VI6_LIF_LBA_LBA0 | (1536 << VI6_LIF_LBA_LBA1_SHIFT)); -- cgit v1.3-14-g43fede From 882bda188f691320a001c6adc738c4a7ec102a8d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 28 Aug 2022 10:13:34 +0200 Subject: media: renesas: vsp1: Add support for RZ/G2L VSPD The RZ/G2L VSPD provides a single VSPD instance. It has the following sub modules MAU, CTU, RPF, DPR, LUT, BRS, WPF and LIF. The VSPD block on RZ/G2L SoCs does not have a version register, so added a new compatible string "renesas,r9a07g044-vsp2" with a data pointer containing the info structure. Also the reset line is shared with the DU module. Signed-off-by: Biju Das Reviewed-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Reviewed-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas/vsp1/vsp1_drv.c | 13 +++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_lif.c | 9 +++++---- drivers/media/platform/renesas/vsp1/vsp1_regs.h | 4 ++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 8ab5b2b37524..c260d318d298 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -830,6 +830,18 @@ static const struct vsp1_device_info vsp1_device_infos[] = { }, }; +static const struct vsp1_device_info rzg2l_vsp2_device_info = { + .version = VI6_IP_VERSION_MODEL_VSPD_RZG2L, + .model = "VSP2-D", + .soc = VI6_IP_VERSION_SOC_RZG2L, + .gen = 3, + .features = VSP1_HAS_BRS | VSP1_HAS_WPF_VFLIP | VSP1_HAS_EXT_DL + | VSP1_HAS_NON_ZERO_LBA, + .lif_count = 1, + .rpf_count = 2, + .wpf_count = 1, +}; + static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) { const struct vsp1_device_info *info; @@ -980,6 +992,7 @@ static int vsp1_remove(struct platform_device *pdev) static const struct of_device_id vsp1_of_match[] = { { .compatible = "renesas,vsp1" }, { .compatible = "renesas,vsp2" }, + { .compatible = "renesas,r9a07g044-vsp2", .data = &rzg2l_vsp2_device_info }, { }, }; MODULE_DEVICE_TABLE(of, vsp1_of_match); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 9adb892edcdc..186a5730e1e3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -107,6 +107,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, case VI6_IP_VERSION_MODEL_VSPDL_GEN3: case VI6_IP_VERSION_MODEL_VSPD_V3: + case VI6_IP_VERSION_MODEL_VSPD_RZG2L: hbth = 0; obth = 1500; lbth = 0; @@ -130,10 +131,10 @@ static void lif_configure_stream(struct vsp1_entity *entity, VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); /* - * On R-Car V3M the LIF0 buffer attribute register has to be set to a - * non-default value to guarantee proper operation (otherwise artifacts - * may appear on the output). The value required by the manual is not - * explained but is likely a buffer size or threshold. + * On R-Car V3M and RZ/G2L the LIF0 buffer attribute register has to be + * set to a non-default value to guarantee proper operation (otherwise + * artifacts may appear on the output). The value required by the + * manual is not explained but is likely a buffer size or threshold. */ if (vsp1_feature(entity->vsp1, VSP1_HAS_NON_ZERO_LBA)) vsp1_lif_write(lif, dlb, VI6_LIF_LBA, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index 4286d13eca32..8928f4c6bb55 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -767,6 +767,8 @@ #define VI6_IP_VERSION_MODEL_VSPDL_GEN3 (0x19 << 8) #define VI6_IP_VERSION_MODEL_VSPBS_GEN3 (0x1a << 8) #define VI6_IP_VERSION_MODEL_VSPD_V3U (0x1c << 8) +/* RZ/G2L SoCs have no version register, So use 0x80 as the model version */ +#define VI6_IP_VERSION_MODEL_VSPD_RZG2L (0x80 << 8) #define VI6_IP_VERSION_SOC_MASK (0xff << 0) #define VI6_IP_VERSION_SOC_H2 (0x01 << 0) @@ -780,6 +782,8 @@ #define VI6_IP_VERSION_SOC_M3N (0x04 << 0) #define VI6_IP_VERSION_SOC_E3 (0x04 << 0) #define VI6_IP_VERSION_SOC_V3U (0x05 << 0) +/* RZ/G2L SoCs have no version register, So use 0x80 for SoC Identification */ +#define VI6_IP_VERSION_SOC_RZG2L (0x80 << 0) #define VI6_IP_VERSION_VSP_SW (0xfffe << 16) /* SW VSP version */ -- cgit v1.3-14-g43fede From f0f078457f18f10696888f8d0e6aba9deb9cde92 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Sat, 8 Jan 2022 18:04:39 +0100 Subject: media: uvcvideo: Fix memory leak in uvc_gpio_parse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the unit buffer was allocated before checking the IRQ for privacy GPIO. In case of error, the unit buffer was leaked. Allocate the unit buffer after the IRQ to avoid it. Addresses-Coverity-ID: 1474639 ("Resource leak") Fixes: 2886477ff987 ("media: uvcvideo: Implement UVC_EXT_GPIO_UNIT") Signed-off-by: José Expósito Reviewed-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 9c05776f11d1..ccc825607437 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1553,10 +1553,6 @@ static int uvc_gpio_parse(struct uvc_device *dev) if (IS_ERR_OR_NULL(gpio_privacy)) return PTR_ERR_OR_ZERO(gpio_privacy); - unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); - if (!unit) - return -ENOMEM; - irq = gpiod_to_irq(gpio_privacy); if (irq < 0) { if (irq != EPROBE_DEFER) @@ -1565,6 +1561,10 @@ static int uvc_gpio_parse(struct uvc_device *dev) return irq; } + unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); + if (!unit) + return -ENOMEM; + unit->gpio.gpio_privacy = gpio_privacy; unit->gpio.irq = irq; unit->gpio.bControlSize = 1; -- cgit v1.3-14-g43fede From c58874df1051c2baee8dabb8c476c3e476923923 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 18 Jul 2022 13:56:54 +0200 Subject: media: uvcvideo: Use indexed loops in uvc_ctrl_init_ctrl() As shown by the bug introduced in commit 86f7ef773156 ("media: uvcvideo: Add support for per-device control mapping overrides"), the loop style used by uvc_ctrl_init_ctrl() is error-prone. Rewrite the loops to use indices instead. Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 8c208db9600b..5c33b0b7ef9a 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2411,10 +2411,9 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl) { - const struct uvc_control_info *info = uvc_ctrls; - const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls); - const struct uvc_control_mapping *mapping; - const struct uvc_control_mapping *mend; + const struct uvc_control_mapping *mappings; + unsigned int num_mappings; + unsigned int i; /* * XU controls initialization requires querying the device for control @@ -2425,7 +2424,9 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) return; - for (; info < iend; ++info) { + for (i = 0; i < ARRAY_SIZE(uvc_ctrls); ++i) { + const struct uvc_control_info *info = &uvc_ctrls[i]; + if (uvc_entity_match_guid(ctrl->entity, info->entity) && ctrl->index == info->index) { uvc_ctrl_add_info(chain->dev, ctrl, info); @@ -2452,9 +2453,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, */ if (chain->dev->info->mappings) { bool custom = false; - unsigned int i; - for (i = 0; (mapping = chain->dev->info->mappings[i]); ++i) { + for (i = 0; chain->dev->info->mappings[i]; ++i) { + const struct uvc_control_mapping *mapping = + chain->dev->info->mappings[i]; + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) { __uvc_ctrl_add_mapping(chain, ctrl, mapping); @@ -2467,10 +2470,9 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, } /* Process common mappings next. */ - mapping = uvc_ctrl_mappings; - mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings); + for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) { + const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i]; - for (; mapping < mend; ++mapping) { if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) __uvc_ctrl_add_mapping(chain, ctrl, mapping); @@ -2478,14 +2480,16 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, /* Finally process version-specific mappings. */ if (chain->dev->uvc_version < 0x0150) { - mapping = uvc_ctrl_mappings_uvc11; - mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc11); + mappings = uvc_ctrl_mappings_uvc11; + num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11); } else { - mapping = uvc_ctrl_mappings_uvc15; - mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc15); + mappings = uvc_ctrl_mappings_uvc15; + num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15); } - for (; mapping < mend; ++mapping) { + for (i = 0; i < num_mappings; ++i) { + const struct uvc_control_mapping *mapping = &mappings[i]; + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) __uvc_ctrl_add_mapping(chain, ctrl, mapping); -- cgit v1.3-14-g43fede From 4c24425488731b401bc64a1ea102ffbd06ae89fd Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Fri, 22 Jul 2022 09:13:43 +0200 Subject: media: uvcvideo: Fix typo 'the the' in comment Replace 'the the' with 'the' in the comment. Signed-off-by: Slark Xiao Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 170a008f4006..d2eb9066e4dc 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1095,7 +1095,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* * Synchronize to the input stream by waiting for the FID bit to be - * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. + * toggled when the buffer state is not UVC_BUF_STATE_ACTIVE. * stream->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. * -- cgit v1.3-14-g43fede From 5f36851c36b30f713f588ed2b60aa7b4512e2c76 Mon Sep 17 00:00:00 2001 From: Yunke Cao Date: Thu, 7 Jul 2022 10:53:31 +0200 Subject: media: uvcvideo: Use entity get_cur in uvc_ctrl_set Entity controls should get_cur using an entity-defined function instead of via a query. Fix this in uvc_ctrl_set. Fixes: 65900c581d01 ("media: uvcvideo: Allow entity-defined get_info and get_cur") Signed-off-by: Yunke Cao Reviewed-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_ctrl.c | 83 ++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 5c33b0b7ef9a..97f312020516 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -985,36 +985,56 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, return value; } -static int __uvc_ctrl_get(struct uvc_video_chain *chain, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 *value) +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain, + struct uvc_control *ctrl) { + u8 *data; int ret; - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) - return -EACCES; + if (ctrl->loaded) + return 0; - if (!ctrl->loaded) { - if (ctrl->entity->get_cur) { - ret = ctrl->entity->get_cur(chain->dev, - ctrl->entity, - ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - } else { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, - ctrl->entity->id, - chain->dev->intfnum, - ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - } - if (ret < 0) - return ret; + data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) { + memset(data, 0, ctrl->info.size); ctrl->loaded = 1; + + return 0; } + if (ctrl->entity->get_cur) + ret = ctrl->entity->get_cur(chain->dev, ctrl->entity, + ctrl->info.selector, data, + ctrl->info.size); + else + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, chain->dev->intfnum, + ctrl->info.selector, data, + ctrl->info.size); + + if (ret < 0) + return ret; + + ctrl->loaded = 1; + + return ret; +} + +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 *value) +{ + int ret; + + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + return -EACCES; + + ret = __uvc_ctrl_load_cur(chain, ctrl); + if (ret < 0) + return ret; + *value = __uvc_ctrl_get_value(mapping, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); @@ -1810,21 +1830,10 @@ int uvc_ctrl_set(struct uvc_fh *handle, * needs to be loaded from the device to perform the read-modify-write * operation. */ - if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) { - if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) { - memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - 0, ctrl->info.size); - } else { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, - ctrl->entity->id, chain->dev->intfnum, - ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - } - - ctrl->loaded = 1; + if ((ctrl->info.size * 8) != mapping->size) { + ret = __uvc_ctrl_load_cur(chain, ctrl); + if (ret < 0) + return ret; } /* Backup the current value in case we need to rollback later. */ -- cgit v1.3-14-g43fede From 101418b374c94f321572d03610f0c9315caf70c7 Mon Sep 17 00:00:00 2001 From: huanglei Date: Sat, 20 Aug 2022 02:56:58 +0200 Subject: media: uvcvideo: Limit power line control for Sonix Technology The device does not implement the power line control correctly. Add a corresponding control mapping override. Bus 003 Device 003: ID 3277:0072 Sonix Technology Co., Ltd. USB 2.0 Camera Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x3277 idProduct 0x0072 bcdDevice 1.00 iManufacturer 2 Sonix Technology Co., Ltd. iProduct 1 USB 2.0 Camera iSerial 3 REV0001 bNumConfigurations 1 Signed-off-by: huanglei Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ccc825607437..639fd7bab5d5 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -3264,6 +3264,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, + /* Sonix Technology USB 2.0 Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x3277, + .idProduct = 0x0072, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Acer EasyCamera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.3-14-g43fede From 546fad2c35562aed7bdc6bb86f03c05e59373219 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Sat, 9 Jul 2022 09:31:00 +0200 Subject: media: staging: media: imx: imx7-media-csi: Increase video mem limit Some high resolution (like Sony IMX492 47Mpix) sensors requre large amount space for buffering. 64MB is far from sufficient so this patch increases the limit to 512MB. Signed-off-by: Petko Manolov Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx7-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index a0553c24cce4..cbc66ef0eda8 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -160,7 +160,7 @@ #define IMX7_CSI_VIDEO_NAME "imx-capture" /* In bytes, per queue */ -#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_64M +#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_512M #define IMX7_CSI_VIDEO_EOF_TIMEOUT 2000 #define IMX7_CSI_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_2X8 -- cgit v1.3-14-g43fede From 1a4f8d7d1874df483e6c6b3685fe2a1f51ed0086 Mon Sep 17 00:00:00 2001 From: Volodymyr Kharuk Date: Thu, 14 Jul 2022 10:34:43 +0200 Subject: media: xilinx: csi2rxss: Add 1X12 greyscale format Extend the csi2rxss with Y12_1X12 greyscale format Signed-off-by: Volodymyr Kharuk Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index cf8e892c47f0..29b53febc2e7 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -188,6 +188,7 @@ static const u32 xcsi2dt_mbus_lut[][2] = { { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_Y12_1X12 }, { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, -- cgit v1.3-14-g43fede From d6a1feba5dcabb6cae9e005714baa80bb37819b1 Mon Sep 17 00:00:00 2001 From: Volodymyr Kharuk Date: Thu, 14 Jul 2022 10:34:44 +0200 Subject: media: xilinx: video: Add 1X12 greyscale format Extend the xilinx video driver with Y12_1X12 greyscale format Signed-off-by: Volodymyr Kharuk Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-vip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index a0073122798f..5b214bf7f93a 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -40,6 +40,8 @@ static const struct xvip_video_format xvip_video_formats[] = { 1, V4L2_PIX_FMT_SGBRG8 }, { XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8 }, + { XVIP_VF_MONO_SENSOR, 12, "mono", MEDIA_BUS_FMT_Y12_1X12, + 2, V4L2_PIX_FMT_Y12 }, }; /** -- cgit v1.3-14-g43fede From 1c78f19c3a0ea312a8178a6bfd8934eb93e9b10a Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Wed, 1 Jun 2022 06:25:14 +0200 Subject: media: xilinx: vipp: Fix refcount leak in xvip_graph_dma_init of_get_child_by_name() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: df3305156f98 ("[media] v4l: xilinx: Add Xilinx Video IP core") Signed-off-by: Miaoqian Lin Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-vipp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index f34f8b077e03..0a16c218a50a 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -471,7 +471,7 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev) { struct device_node *ports; struct device_node *port; - int ret; + int ret = 0; ports = of_get_child_by_name(xdev->dev->of_node, "ports"); if (ports == NULL) { @@ -481,13 +481,14 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev) for_each_child_of_node(ports, port) { ret = xvip_graph_dma_init_one(xdev, port); - if (ret < 0) { + if (ret) { of_node_put(port); - return ret; + break; } } - return 0; + of_node_put(ports); + return ret; } static void xvip_graph_cleanup(struct xvip_composite_device *xdev) -- cgit v1.3-14-g43fede From 4ad7b39623ab04f8366fbbbbd3caea86aacf5f12 Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Tue, 23 Aug 2022 04:38:00 +0200 Subject: media: dt-binding: mediatek: add bindings for MediaTek MDP3 components This patch adds DT binding documents for Media Data Path 3 (MDP3) a unit in multimedia system combined with several components and used for scaling and color format convert. Signed-off-by: Moudy Ho Reviewed-by: Rob Herring Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/mediatek,mdp3-rdma.yaml | 95 ++++++++++++++++++++++ .../bindings/media/mediatek,mdp3-rsz.yaml | 77 ++++++++++++++++++ .../bindings/media/mediatek,mdp3-wrot.yaml | 80 ++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml create mode 100644 Documentation/devicetree/bindings/media/mediatek,mdp3-rsz.yaml create mode 100644 Documentation/devicetree/bindings/media/mediatek,mdp3-wrot.yaml diff --git a/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml b/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml new file mode 100644 index 000000000000..9cfc0c7d23e0 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/mediatek,mdp3-rdma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Read Direct Memory Access + +maintainers: + - Matthias Brugger + - Moudy Ho + +description: | + MediaTek Read Direct Memory Access(RDMA) component used to do read DMA. + It contains one line buffer to store the sufficient pixel data, and + must be siblings to the central MMSYS_CONFIG node. + For a description of the MMSYS_CONFIG binding, see + Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml + for details. + +properties: + compatible: + items: + - const: mediatek,mt8183-mdp3-rdma + + reg: + maxItems: 1 + + mediatek,gce-client-reg: + $ref: '/schemas/types.yaml#/definitions/phandle-array' + items: + items: + - description: phandle of GCE + - description: GCE subsys id + - description: register offset + - description: register size + description: The register of client driver can be configured by gce with + 4 arguments defined in this property. Each GCE subsys id is mapping to + a client defined in the header include/dt-bindings/gce/-gce.h. + + mediatek,gce-events: + description: + The event id which is mapping to the specific hardware event signal + to gce. The event id is defined in the gce header + include/dt-bindings/gce/-gce.h of each chips. + $ref: /schemas/types.yaml#/definitions/uint32-array + + power-domains: + maxItems: 1 + + clocks: + items: + - description: RDMA clock + - description: RSZ clock + + iommus: + maxItems: 1 + + mboxes: + items: + - description: used for 1st data pipe from RDMA + - description: used for 2nd data pipe from RDMA + +required: + - compatible + - reg + - mediatek,gce-client-reg + - mediatek,gce-events + - power-domains + - clocks + - iommus + - mboxes + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + mdp3_rdma0: mdp3-rdma0@14001000 { + compatible = "mediatek,mt8183-mdp3-rdma"; + reg = <0x14001000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1400XXXX 0x1000 0x1000>; + mediatek,gce-events = , + ; + power-domains = <&spm MT8183_POWER_DOMAIN_DISP>; + clocks = <&mmsys CLK_MM_MDP_RDMA0>, + <&mmsys CLK_MM_MDP_RSZ1>; + iommus = <&iommu>; + mboxes = <&gce 20 CMDQ_THR_PRIO_LOWEST>, + <&gce 21 CMDQ_THR_PRIO_LOWEST>; + }; diff --git a/Documentation/devicetree/bindings/media/mediatek,mdp3-rsz.yaml b/Documentation/devicetree/bindings/media/mediatek,mdp3-rsz.yaml new file mode 100644 index 000000000000..78f9de6192ef --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek,mdp3-rsz.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/mediatek,mdp3-rsz.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Resizer + +maintainers: + - Matthias Brugger + - Moudy Ho + +description: | + One of Media Data Path 3 (MDP3) components used to do frame resizing. + +properties: + compatible: + items: + - enum: + - mediatek,mt8183-mdp3-rsz + + reg: + maxItems: 1 + + mediatek,gce-client-reg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle of GCE + - description: GCE subsys id + - description: register offset + - description: register size + description: The register of client driver can be configured by gce with + 4 arguments defined in this property. Each GCE subsys id is mapping to + a client defined in the header include/dt-bindings/gce/-gce.h. + + mediatek,gce-events: + description: + The event id which is mapping to the specific hardware event signal + to gce. The event id is defined in the gce header + include/dt-bindings/gce/-gce.h of each chips. + $ref: /schemas/types.yaml#/definitions/uint32-array + + clocks: + minItems: 1 + +required: + - compatible + - reg + - mediatek,gce-client-reg + - mediatek,gce-events + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + + mdp3_rsz0: mdp3-rsz0@14003000 { + compatible = "mediatek,mt8183-mdp3-rsz"; + reg = <0x14003000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1400XXXX 0x3000 0x1000>; + mediatek,gce-events = , + ; + clocks = <&mmsys CLK_MM_MDP_RSZ0>; + }; + + mdp3_rsz1: mdp3-rsz1@14004000 { + compatible = "mediatek,mt8183-mdp3-rsz"; + reg = <0x14004000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1400XXXX 0x4000 0x1000>; + mediatek,gce-events = , + ; + clocks = <&mmsys CLK_MM_MDP_RSZ1>; + }; diff --git a/Documentation/devicetree/bindings/media/mediatek,mdp3-wrot.yaml b/Documentation/devicetree/bindings/media/mediatek,mdp3-wrot.yaml new file mode 100644 index 000000000000..0baa77198fa2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek,mdp3-wrot.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/mediatek,mdp3-wrot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Write DMA with Rotation + +maintainers: + - Matthias Brugger + - Moudy Ho + +description: | + One of Media Data Path 3 (MDP3) components used to write DMA with frame rotation. + +properties: + compatible: + items: + - enum: + - mediatek,mt8183-mdp3-wrot + + reg: + maxItems: 1 + + mediatek,gce-client-reg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle of GCE + - description: GCE subsys id + - description: register offset + - description: register size + description: The register of client driver can be configured by gce with + 4 arguments defined in this property. Each GCE subsys id is mapping to + a client defined in the header include/dt-bindings/gce/-gce.h. + + mediatek,gce-events: + description: + The event id which is mapping to the specific hardware event signal + to gce. The event id is defined in the gce header + include/dt-bindings/gce/-gce.h of each chips. + $ref: /schemas/types.yaml#/definitions/uint32-array + + power-domains: + maxItems: 1 + + clocks: + minItems: 1 + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - mediatek,gce-client-reg + - mediatek,gce-events + - power-domains + - clocks + - iommus + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + mdp3_wrot0: mdp3-wrot0@14005000 { + compatible = "mediatek,mt8183-mdp3-wrot"; + reg = <0x14005000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1400XXXX 0x5000 0x1000>; + mediatek,gce-events = , + ; + power-domains = <&spm MT8183_POWER_DOMAIN_DISP>; + clocks = <&mmsys CLK_MM_MDP_WROT0>; + iommus = <&iommu>; + }; -- cgit v1.3-14-g43fede From 8bbdead4dede97d8737bd4d164ff9c0a7d03a4b8 Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Tue, 23 Aug 2022 04:38:01 +0200 Subject: media: dt-binding: mediatek: add bindings for MediaTek CCORR and WDMA This patch adds DT binding documentation for MediaTek's CCORR and WDMA components. These components exist in both MediaTek's Media Data Path 3(MDP3) and DRM, and the bindings are placed under the folder "./soc/mediatek" to prevent duplicate builds. Signed-off-by: Moudy Ho Reviewed-by: Rob Herring Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/soc/mediatek/mediatek,ccorr.yaml | 68 ++++++++++++++++++ .../bindings/soc/mediatek/mediatek,wdma.yaml | 81 ++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,ccorr.yaml create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,wdma.yaml diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,ccorr.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,ccorr.yaml new file mode 100644 index 000000000000..4380b98b0dfe --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,ccorr.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/mediatek/mediatek,ccorr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek color correction + +maintainers: + - Matthias Brugger + - Moudy Ho + +description: | + MediaTek color correction with 3X3 matrix. + +properties: + compatible: + items: + - enum: + - mediatek,mt8183-mdp3-ccorr + + reg: + maxItems: 1 + + mediatek,gce-client-reg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle of GCE + - description: GCE subsys id + - description: register offset + - description: register size + description: The register of client driver can be configured by gce with + 4 arguments defined in this property. Each GCE subsys id is mapping to + a client defined in the header include/dt-bindings/gce/-gce.h. + + mediatek,gce-events: + description: + The event id which is mapping to the specific hardware event signal + to gce. The event id is defined in the gce header + include/dt-bindings/gce/-gce.h of each chips. + $ref: /schemas/types.yaml#/definitions/uint32-array + + clocks: + minItems: 1 + +required: + - compatible + - reg + - mediatek,gce-client-reg + - mediatek,gce-events + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + + mdp3_ccorr: mdp3-ccorr@1401c000 { + compatible = "mediatek,mt8183-mdp3-ccorr"; + reg = <0x1401c000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1401XXXX 0xc000 0x1000>; + mediatek,gce-events = , + ; + clocks = <&mmsys CLK_MM_MDP_CCORR>; + }; diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,wdma.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,wdma.yaml new file mode 100644 index 000000000000..69afb329e5f4 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,wdma.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/mediatek/mediatek,wdma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Write Direct Memory Access + +maintainers: + - Matthias Brugger + - Moudy Ho + +description: | + MediaTek Write Direct Memory Access(WDMA) component used to write + the data into DMA. + +properties: + compatible: + items: + - enum: + - mediatek,mt8183-mdp3-wdma + + reg: + maxItems: 1 + + mediatek,gce-client-reg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle of GCE + - description: GCE subsys id + - description: register offset + - description: register size + description: The register of client driver can be configured by gce with + 4 arguments defined in this property. Each GCE subsys id is mapping to + a client defined in the header include/dt-bindings/gce/-gce.h. + + mediatek,gce-events: + description: + The event id which is mapping to the specific hardware event signal + to gce. The event id is defined in the gce header + include/dt-bindings/gce/-gce.h of each chips. + $ref: /schemas/types.yaml#/definitions/uint32-array + + power-domains: + maxItems: 1 + + clocks: + minItems: 1 + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - mediatek,gce-client-reg + - mediatek,gce-events + - power-domains + - clocks + - iommus + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + mdp3_wdma: mdp3-wdma@14006000 { + compatible = "mediatek,mt8183-mdp3-wdma"; + reg = <0x14006000 0x1000>; + mediatek,gce-client-reg = <&gce SUBSYS_1400XXXX 0x6000 0x1000>; + mediatek,gce-events = , + ; + power-domains = <&spm MT8183_POWER_DOMAIN_DISP>; + clocks = <&mmsys CLK_MM_MDP_WDMA0>; + iommus = <&iommu>; + }; -- cgit v1.3-14-g43fede From 61890ccaefaff89f5babd2c8412fd222c3f5fe38 Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Tue, 23 Aug 2022 04:38:03 +0200 Subject: media: platform: mtk-mdp3: add MediaTek MDP3 driver This patch adds driver for MediaTek's Media Data Path ver.3 (MDP3). It provides the following functions: color transform, format conversion, resize, crop, rotate, flip and additional image quality enhancement. The MDP3 driver is mainly used for Google Chromebook products to import the new architecture to set the HW settings as shown below: User -> V4L2 framework -> MDP3 driver -> SCP (setting calculations) -> MDP3 driver -> CMDQ (GCE driver) -> HW Each modules' related operation control is sited in mtk-mdp3-comp.c Each modules' register table is defined in file with "mdp_reg_" prefix GCE related API, operation control sited in mtk-mdp3-cmdq.c V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c Probe, power, suspend/resume, system level functions are defined in mtk-mdp3-core.c [hverkuil: add 'depends on REMOTEPROC'] Signed-off-by: Ping-Hsun Wu Signed-off-by: daoyuan huang Signed-off-by: Moudy Ho Tested-by: AngeloGioacchino Del Regno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/Kconfig | 1 + drivers/media/platform/mediatek/Makefile | 1 + drivers/media/platform/mediatek/mdp3/Kconfig | 21 + drivers/media/platform/mediatek/mdp3/Makefile | 6 + .../media/platform/mediatek/mdp3/mdp_reg_ccorr.h | 19 + .../media/platform/mediatek/mdp3/mdp_reg_rdma.h | 65 ++ drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h | 39 + .../media/platform/mediatek/mdp3/mdp_reg_wdma.h | 47 + .../media/platform/mediatek/mdp3/mdp_reg_wrot.h | 55 ++ drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h | 290 ++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c | 466 +++++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h | 43 + .../media/platform/mediatek/mdp3/mtk-mdp3-comp.c | 1033 ++++++++++++++++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-comp.h | 186 ++++ .../media/platform/mediatek/mdp3/mtk-mdp3-core.c | 357 +++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-core.h | 94 ++ .../media/platform/mediatek/mdp3/mtk-mdp3-m2m.c | 724 ++++++++++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-m2m.h | 48 + .../media/platform/mediatek/mdp3/mtk-mdp3-regs.c | 735 ++++++++++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-regs.h | 373 +++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-vpu.c | 313 ++++++ .../media/platform/mediatek/mdp3/mtk-mdp3-vpu.h | 78 ++ 22 files changed, 4994 insertions(+) create mode 100644 drivers/media/platform/mediatek/mdp3/Kconfig create mode 100644 drivers/media/platform/mediatek/mdp3/Makefile create mode 100644 drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h create mode 100644 drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h create mode 100644 drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h create mode 100644 drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h create mode 100644 drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c create mode 100644 drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig index af47d9888552..84104e2cd024 100644 --- a/drivers/media/platform/mediatek/Kconfig +++ b/drivers/media/platform/mediatek/Kconfig @@ -6,3 +6,4 @@ source "drivers/media/platform/mediatek/jpeg/Kconfig" source "drivers/media/platform/mediatek/mdp/Kconfig" source "drivers/media/platform/mediatek/vcodec/Kconfig" source "drivers/media/platform/mediatek/vpu/Kconfig" +source "drivers/media/platform/mediatek/mdp3/Kconfig" diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile index d3850a13f128..38e6ba917fe5 100644 --- a/drivers/media/platform/mediatek/Makefile +++ b/drivers/media/platform/mediatek/Makefile @@ -3,3 +3,4 @@ obj-y += jpeg/ obj-y += mdp/ obj-y += vcodec/ obj-y += vpu/ +obj-y += mdp3/ diff --git a/drivers/media/platform/mediatek/mdp3/Kconfig b/drivers/media/platform/mediatek/mdp3/Kconfig new file mode 100644 index 000000000000..50ae07b75b5f --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEDIATEK_MDP3 + tristate "MediaTek MDP v3 driver" + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_DMA + depends on REMOTEPROC + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select MTK_MMSYS + select VIDEO_MEDIATEK_VPU + select MTK_CMDQ + select MTK_SCP + default n + help + It is a v4l2 driver and present in MediaTek MT8183 SoC. + The driver supports scaling and color space conversion. + + To compile this driver as a module, choose M here: the + module will be called mtk-mdp3. diff --git a/drivers/media/platform/mediatek/mdp3/Makefile b/drivers/media/platform/mediatek/mdp3/Makefile new file mode 100644 index 000000000000..63e6c87e480b --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +mtk-mdp3-y += mtk-mdp3-core.o mtk-mdp3-vpu.o mtk-mdp3-regs.o +mtk-mdp3-y += mtk-mdp3-m2m.o +mtk-mdp3-y += mtk-mdp3-comp.o mtk-mdp3-cmdq.o + +obj-$(CONFIG_VIDEO_MEDIATEK_MDP3) += mtk-mdp3.o diff --git a/drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h b/drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h new file mode 100644 index 000000000000..3b2c6531c194 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MDP_REG_CCORR_H__ +#define __MDP_REG_CCORR_H__ + +#define MDP_CCORR_EN 0x000 +#define MDP_CCORR_CFG 0x020 +#define MDP_CCORR_SIZE 0x030 + +/* MASK */ +#define MDP_CCORR_EN_MASK 0x00000001 +#define MDP_CCORR_CFG_MASK 0x70001317 +#define MDP_CCORR_SIZE_MASK 0x1fff1fff + +#endif // __MDP_REG_CCORR_H__ diff --git a/drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h b/drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h new file mode 100644 index 000000000000..be4065e252d3 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MDP_REG_RDMA_H__ +#define __MDP_REG_RDMA_H__ + +#define MDP_RDMA_EN 0x000 +#define MDP_RDMA_RESET 0x008 +#define MDP_RDMA_CON 0x020 +#define MDP_RDMA_GMCIF_CON 0x028 +#define MDP_RDMA_SRC_CON 0x030 +#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE 0x060 +#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL 0x068 +#define MDP_RDMA_MF_SRC_SIZE 0x070 +#define MDP_RDMA_MF_CLIP_SIZE 0x078 +#define MDP_RDMA_MF_OFFSET_1 0x080 +#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE 0x090 +#define MDP_RDMA_SRC_END_0 0x100 +#define MDP_RDMA_SRC_END_1 0x108 +#define MDP_RDMA_SRC_END_2 0x110 +#define MDP_RDMA_SRC_OFFSET_0 0x118 +#define MDP_RDMA_SRC_OFFSET_1 0x120 +#define MDP_RDMA_SRC_OFFSET_2 0x128 +#define MDP_RDMA_SRC_OFFSET_0_P 0x148 +#define MDP_RDMA_TRANSFORM_0 0x200 +#define MDP_RDMA_RESV_DUMMY_0 0x2a0 +#define MDP_RDMA_MON_STA_1 0x408 +#define MDP_RDMA_SRC_BASE_0 0xf00 +#define MDP_RDMA_SRC_BASE_1 0xf08 +#define MDP_RDMA_SRC_BASE_2 0xf10 +#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y 0xf20 +#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C 0xf28 + +/* MASK */ +#define MDP_RDMA_EN_MASK 0x00000001 +#define MDP_RDMA_RESET_MASK 0x00000001 +#define MDP_RDMA_CON_MASK 0x00001110 +#define MDP_RDMA_GMCIF_CON_MASK 0xfffb3771 +#define MDP_RDMA_SRC_CON_MASK 0xf3ffffff +#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE_MASK 0x001fffff +#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL_MASK 0x001fffff +#define MDP_RDMA_MF_SRC_SIZE_MASK 0x1fff1fff +#define MDP_RDMA_MF_CLIP_SIZE_MASK 0x1fff1fff +#define MDP_RDMA_MF_OFFSET_1_MASK 0x003f001f +#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE_MASK 0x001fffff +#define MDP_RDMA_SRC_END_0_MASK 0xffffffff +#define MDP_RDMA_SRC_END_1_MASK 0xffffffff +#define MDP_RDMA_SRC_END_2_MASK 0xffffffff +#define MDP_RDMA_SRC_OFFSET_0_MASK 0xffffffff +#define MDP_RDMA_SRC_OFFSET_1_MASK 0xffffffff +#define MDP_RDMA_SRC_OFFSET_2_MASK 0xffffffff +#define MDP_RDMA_SRC_OFFSET_0_P_MASK 0xffffffff +#define MDP_RDMA_TRANSFORM_0_MASK 0xff110777 +#define MDP_RDMA_RESV_DUMMY_0_MASK 0xffffffff +#define MDP_RDMA_MON_STA_1_MASK 0xffffffff +#define MDP_RDMA_SRC_BASE_0_MASK 0xffffffff +#define MDP_RDMA_SRC_BASE_1_MASK 0xffffffff +#define MDP_RDMA_SRC_BASE_2_MASK 0xffffffff +#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y_MASK 0xffffffff +#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C_MASK 0xffffffff + +#endif // __MDP_REG_RDMA_H__ diff --git a/drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h b/drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h new file mode 100644 index 000000000000..484f6d60641f --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MDP_REG_RSZ_H__ +#define __MDP_REG_RSZ_H__ + +#define PRZ_ENABLE 0x000 +#define PRZ_CONTROL_1 0x004 +#define PRZ_CONTROL_2 0x008 +#define PRZ_INPUT_IMAGE 0x010 +#define PRZ_OUTPUT_IMAGE 0x014 +#define PRZ_HORIZONTAL_COEFF_STEP 0x018 +#define PRZ_VERTICAL_COEFF_STEP 0x01c +#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET 0x020 +#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET 0x024 +#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET 0x028 +#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET 0x02c +#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET 0x030 +#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET 0x034 + +/* MASK */ +#define PRZ_ENABLE_MASK 0x00010001 +#define PRZ_CONTROL_1_MASK 0xfffffff3 +#define PRZ_CONTROL_2_MASK 0x0ffffaff +#define PRZ_INPUT_IMAGE_MASK 0xffffffff +#define PRZ_OUTPUT_IMAGE_MASK 0xffffffff +#define PRZ_HORIZONTAL_COEFF_STEP_MASK 0x007fffff +#define PRZ_VERTICAL_COEFF_STEP_MASK 0x007fffff +#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET_MASK 0x0000ffff +#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK 0x001fffff +#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET_MASK 0x0000ffff +#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET_MASK 0x001fffff +#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET_MASK 0x0000ffff +#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK 0x001fffff + +#endif // __MDP_REG_RSZ_H__ diff --git a/drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h b/drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h new file mode 100644 index 000000000000..0280e91c09e4 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MDP_REG_WDMA_H__ +#define __MDP_REG_WDMA_H__ + +#define WDMA_EN 0x008 +#define WDMA_RST 0x00c +#define WDMA_CFG 0x014 +#define WDMA_SRC_SIZE 0x018 +#define WDMA_CLIP_SIZE 0x01c +#define WDMA_CLIP_COORD 0x020 +#define WDMA_DST_W_IN_BYTE 0x028 +#define WDMA_ALPHA 0x02c +#define WDMA_BUF_CON2 0x03c +#define WDMA_DST_UV_PITCH 0x078 +#define WDMA_DST_ADDR_OFFSET 0x080 +#define WDMA_DST_U_ADDR_OFFSET 0x084 +#define WDMA_DST_V_ADDR_OFFSET 0x088 +#define WDMA_FLOW_CTRL_DBG 0x0a0 +#define WDMA_DST_ADDR 0xf00 +#define WDMA_DST_U_ADDR 0xf04 +#define WDMA_DST_V_ADDR 0xf08 + +/* MASK */ +#define WDMA_EN_MASK 0x00000001 +#define WDMA_RST_MASK 0x00000001 +#define WDMA_CFG_MASK 0xff03bff0 +#define WDMA_SRC_SIZE_MASK 0x3fff3fff +#define WDMA_CLIP_SIZE_MASK 0x3fff3fff +#define WDMA_CLIP_COORD_MASK 0x3fff3fff +#define WDMA_DST_W_IN_BYTE_MASK 0x0000ffff +#define WDMA_ALPHA_MASK 0x800000ff +#define WDMA_BUF_CON2_MASK 0xffffffff +#define WDMA_DST_UV_PITCH_MASK 0x0000ffff +#define WDMA_DST_ADDR_OFFSET_MASK 0x0fffffff +#define WDMA_DST_U_ADDR_OFFSET_MASK 0x0fffffff +#define WDMA_DST_V_ADDR_OFFSET_MASK 0x0fffffff +#define WDMA_FLOW_CTRL_DBG_MASK 0x0000f3ff +#define WDMA_DST_ADDR_MASK 0xffffffff +#define WDMA_DST_U_ADDR_MASK 0xffffffff +#define WDMA_DST_V_ADDR_MASK 0xffffffff + +#endif // __MDP_REG_WDMA_H__ diff --git a/drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h b/drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h new file mode 100644 index 000000000000..6d3ff0e2b672 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MDP_REG_WROT_H__ +#define __MDP_REG_WROT_H__ + +#define VIDO_CTRL 0x000 +#define VIDO_MAIN_BUF_SIZE 0x008 +#define VIDO_SOFT_RST 0x010 +#define VIDO_SOFT_RST_STAT 0x014 +#define VIDO_CROP_OFST 0x020 +#define VIDO_TAR_SIZE 0x024 +#define VIDO_OFST_ADDR 0x02c +#define VIDO_STRIDE 0x030 +#define VIDO_OFST_ADDR_C 0x038 +#define VIDO_STRIDE_C 0x03c +#define VIDO_DITHER 0x054 +#define VIDO_STRIDE_V 0x06c +#define VIDO_OFST_ADDR_V 0x068 +#define VIDO_RSV_1 0x070 +#define VIDO_IN_SIZE 0x078 +#define VIDO_ROT_EN 0x07c +#define VIDO_FIFO_TEST 0x080 +#define VIDO_MAT_CTRL 0x084 +#define VIDO_BASE_ADDR 0xf00 +#define VIDO_BASE_ADDR_C 0xf04 +#define VIDO_BASE_ADDR_V 0xf08 + +/* MASK */ +#define VIDO_CTRL_MASK 0xf530711f +#define VIDO_MAIN_BUF_SIZE_MASK 0x1fff7f77 +#define VIDO_SOFT_RST_MASK 0x00000001 +#define VIDO_SOFT_RST_STAT_MASK 0x00000001 +#define VIDO_TAR_SIZE_MASK 0x1fff1fff +#define VIDO_CROP_OFST_MASK 0x1fff1fff +#define VIDO_OFST_ADDR_MASK 0x0fffffff +#define VIDO_STRIDE_MASK 0x0000ffff +#define VIDO_OFST_ADDR_C_MASK 0x0fffffff +#define VIDO_STRIDE_C_MASK 0x0000ffff +#define VIDO_DITHER_MASK 0xff000001 +#define VIDO_STRIDE_V_MASK 0x0000ffff +#define VIDO_OFST_ADDR_V_MASK 0x0fffffff +#define VIDO_RSV_1_MASK 0xffffffff +#define VIDO_IN_SIZE_MASK 0x1fff1fff +#define VIDO_ROT_EN_MASK 0x00000001 +#define VIDO_FIFO_TEST_MASK 0x00000fff +#define VIDO_MAT_CTRL_MASK 0x000000f3 +#define VIDO_BASE_ADDR_MASK 0xffffffff +#define VIDO_BASE_ADDR_C_MASK 0xffffffff +#define VIDO_BASE_ADDR_V_MASK 0xffffffff + +#endif // __MDP_REG_WROT_H__ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h new file mode 100644 index 000000000000..3e66ebaee2da --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Holmes Chiou + * Ping-Hsun Wu + */ + +#ifndef __MTK_IMG_IPI_H__ +#define __MTK_IMG_IPI_H__ + +#include + +/* + * ISP-MDP generic input information + * MD5 of the target SCP blob: + * 6da52bdcf4bf76a0983b313e1d4745d6 + */ + +#define IMG_MAX_HW_INPUTS 3 + +#define IMG_MAX_HW_OUTPUTS 4 + +#define IMG_MAX_PLANES 3 + +#define IMG_IPI_INIT 1 +#define IMG_IPI_DEINIT 2 +#define IMG_IPI_FRAME 3 +#define IMG_IPI_DEBUG 4 + +struct img_timeval { + u32 tv_sec; + u32 tv_usec; +} __packed; + +struct img_addr { + u64 va; /* Used for Linux OS access */ + u32 pa; /* Used for CM4 access */ + u32 iova; /* Used for IOMMU HW access */ +} __packed; + +struct tuning_addr { + u64 present; + u32 pa; /* Used for CM4 access */ + u32 iova; /* Used for IOMMU HW access */ +} __packed; + +struct img_sw_addr { + u64 va; /* Used for APMCU access */ + u32 pa; /* Used for CM4 access */ +} __packed; + +struct img_plane_format { + u32 size; + u16 stride; +} __packed; + +struct img_pix_format { + u16 width; + u16 height; + u32 colorformat; /* enum mdp_color */ + u16 ycbcr_prof; /* enum mdp_ycbcr_profile */ + struct img_plane_format plane_fmt[IMG_MAX_PLANES]; +} __packed; + +struct img_image_buffer { + struct img_pix_format format; + u32 iova[IMG_MAX_PLANES]; + /* enum mdp_buffer_usage, FD or advanced ISP usages */ + u32 usage; +} __packed; + +#define IMG_SUBPIXEL_SHIFT 20 + +struct img_crop { + s16 left; + s16 top; + u16 width; + u16 height; + u32 left_subpix; + u32 top_subpix; + u32 width_subpix; + u32 height_subpix; +} __packed; + +#define IMG_CTRL_FLAG_HFLIP BIT(0) +#define IMG_CTRL_FLAG_DITHER BIT(1) +#define IMG_CTRL_FLAG_SHARPNESS BIT(4) +#define IMG_CTRL_FLAG_HDR BIT(5) +#define IMG_CTRL_FLAG_DRE BIT(6) + +struct img_input { + struct img_image_buffer buffer; + u16 flags; /* HDR, DRE, dither */ +} __packed; + +struct img_output { + struct img_image_buffer buffer; + struct img_crop crop; + s16 rotation; + u16 flags; /* H-flip, sharpness, dither */ +} __packed; + +struct img_ipi_frameparam { + u32 index; + u32 frame_no; + struct img_timeval timestamp; + u8 type; /* enum mdp_stream_type */ + u8 state; + u8 num_inputs; + u8 num_outputs; + u64 drv_data; + struct img_input inputs[IMG_MAX_HW_INPUTS]; + struct img_output outputs[IMG_MAX_HW_OUTPUTS]; + struct tuning_addr tuning_data; + struct img_addr subfrm_data; + struct img_sw_addr config_data; + struct img_sw_addr self_data; +} __packed; + +struct img_sw_buffer { + u64 handle; /* Used for APMCU access */ + u32 scp_addr; /* Used for CM4 access */ +} __packed; + +struct img_ipi_param { + u8 usage; + struct img_sw_buffer frm_param; +} __packed; + +struct img_frameparam { + struct list_head list_entry; + struct img_ipi_frameparam frameparam; +}; + +/* ISP-MDP generic output information */ + +struct img_comp_frame { + u32 output_disable:1; + u32 bypass:1; + u16 in_width; + u16 in_height; + u16 out_width; + u16 out_height; + struct img_crop crop; + u16 in_total_width; + u16 out_total_width; +} __packed; + +struct img_region { + s16 left; + s16 right; + s16 top; + s16 bottom; +} __packed; + +struct img_offset { + s16 left; + s16 top; + u32 left_subpix; + u32 top_subpix; +} __packed; + +struct img_comp_subfrm { + u32 tile_disable:1; + struct img_region in; + struct img_region out; + struct img_offset luma; + struct img_offset chroma; + s16 out_vertical; /* Output vertical index */ + s16 out_horizontal; /* Output horizontal index */ +} __packed; + +#define IMG_MAX_SUBFRAMES 14 + +struct mdp_rdma_subfrm { + u32 offset[IMG_MAX_PLANES]; + u32 offset_0_p; + u32 src; + u32 clip; + u32 clip_ofst; +} __packed; + +struct mdp_rdma_data { + u32 src_ctrl; + u32 control; + u32 iova[IMG_MAX_PLANES]; + u32 iova_end[IMG_MAX_PLANES]; + u32 mf_bkgd; + u32 mf_bkgd_in_pxl; + u32 sf_bkgd; + u32 ufo_dec_y; + u32 ufo_dec_c; + u32 transform; + struct mdp_rdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; +} __packed; + +struct mdp_rsz_subfrm { + u32 control2; + u32 src; + u32 clip; +} __packed; + +struct mdp_rsz_data { + u32 coeff_step_x; + u32 coeff_step_y; + u32 control1; + u32 control2; + struct mdp_rsz_subfrm subfrms[IMG_MAX_SUBFRAMES]; +} __packed; + +struct mdp_wrot_subfrm { + u32 offset[IMG_MAX_PLANES]; + u32 src; + u32 clip; + u32 clip_ofst; + u32 main_buf; +} __packed; + +struct mdp_wrot_data { + u32 iova[IMG_MAX_PLANES]; + u32 control; + u32 stride[IMG_MAX_PLANES]; + u32 mat_ctrl; + u32 fifo_test; + u32 filter; + struct mdp_wrot_subfrm subfrms[IMG_MAX_SUBFRAMES]; +} __packed; + +struct mdp_wdma_subfrm { + u32 offset[IMG_MAX_PLANES]; + u32 src; + u32 clip; + u32 clip_ofst; +} __packed; + +struct mdp_wdma_data { + u32 wdma_cfg; + u32 iova[IMG_MAX_PLANES]; + u32 w_in_byte; + u32 uv_stride; + struct mdp_wdma_subfrm subfrms[IMG_MAX_SUBFRAMES]; +} __packed; + +struct isp_data { + u64 dl_flags; /* 1 << (enum mdp_comp_type) */ + u32 smxi_iova[4]; + u32 cq_idx; + u32 cq_iova; + u32 tpipe_iova[IMG_MAX_SUBFRAMES]; +} __packed; + +struct img_compparam { + u16 type; /* enum mdp_comp_type */ + u16 id; /* enum mtk_mdp_comp_id */ + u32 input; + u32 outputs[IMG_MAX_HW_OUTPUTS]; + u32 num_outputs; + struct img_comp_frame frame; + struct img_comp_subfrm subfrms[IMG_MAX_SUBFRAMES]; + u32 num_subfrms; + union { + struct mdp_rdma_data rdma; + struct mdp_rsz_data rsz; + struct mdp_wrot_data wrot; + struct mdp_wdma_data wdma; + struct isp_data isp; + }; +} __packed; + +#define IMG_MAX_COMPONENTS 20 + +struct img_mux { + u32 reg; + u32 value; + u32 subsys_id; +}; + +struct img_mmsys_ctrl { + struct img_mux sets[IMG_MAX_COMPONENTS * 2]; + u32 num_sets; +}; + +struct img_config { + struct img_compparam components[IMG_MAX_COMPONENTS]; + u32 num_components; + struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES]; + u32 num_subfrms; +} __packed; + +#endif /* __MTK_IMG_IPI_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c new file mode 100644 index 000000000000..29f6c1cd3de7 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include "mtk-mdp3-cmdq.h" +#include "mtk-mdp3-comp.h" +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-m2m.h" + +#define MDP_PATH_MAX_COMPS IMG_MAX_COMPONENTS + +struct mdp_path { + struct mdp_dev *mdp_dev; + struct mdp_comp_ctx comps[MDP_PATH_MAX_COMPS]; + u32 num_comps; + const struct img_config *config; + const struct img_ipi_frameparam *param; + const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS]; + struct v4l2_rect bounds[IMG_MAX_HW_OUTPUTS]; +}; + +#define has_op(ctx, op) \ + ((ctx)->comp->ops && (ctx)->comp->ops->op) + #define call_op(ctx, op, ...) \ + (has_op(ctx, op) ? (ctx)->comp->ops->op(ctx, ##__VA_ARGS__) : 0) + +static bool is_output_disabled(const struct img_compparam *param, u32 count) +{ + return (count < param->num_subfrms) ? + (param->frame.output_disable || + param->subfrms[count].tile_disable) : + true; +} + +static int mdp_path_subfrm_require(const struct mdp_path *path, + struct mdp_cmdq_cmd *cmd, + s32 *mutex_id, u32 count) +{ + const struct img_config *config = path->config; + const struct mdp_comp_ctx *ctx; + const struct mtk_mdp_driver_data *data = path->mdp_dev->mdp_data; + struct device *dev = &path->mdp_dev->pdev->dev; + struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; + int id, index; + + /* Decide which mutex to use based on the current pipeline */ + switch (path->comps[0].comp->id) { + case MDP_COMP_RDMA0: + *mutex_id = MDP_PIPE_RDMA0; + break; + case MDP_COMP_ISP_IMGI: + *mutex_id = MDP_PIPE_IMGI; + break; + case MDP_COMP_WPEI: + *mutex_id = MDP_PIPE_WPEI; + break; + case MDP_COMP_WPEI2: + *mutex_id = MDP_PIPE_WPEI2; + break; + default: + dev_err(dev, "Unknown pipeline and no mutex is assigned"); + return -EINVAL; + } + + /* Set mutex mod */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + if (is_output_disabled(ctx->param, count)) + continue; + id = ctx->comp->id; + mtk_mutex_write_mod(mutex[*mutex_id], + data->mdp_mutex_table_idx[id], false); + } + + mtk_mutex_write_sof(mutex[*mutex_id], + MUTEX_SOF_IDX_SINGLE_MODE); + + return 0; +} + +static int mdp_path_subfrm_run(const struct mdp_path *path, + struct mdp_cmdq_cmd *cmd, + s32 *mutex_id, u32 count) +{ + const struct img_config *config = path->config; + const struct mdp_comp_ctx *ctx; + struct device *dev = &path->mdp_dev->pdev->dev; + struct mtk_mutex **mutex = path->mdp_dev->mdp_mutex; + int index; + s32 event; + + if (-1 == *mutex_id) { + dev_err(dev, "Incorrect mutex id"); + return -EINVAL; + } + + /* Wait WROT SRAM shared to DISP RDMA */ + /* Clear SOF event for each engine */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + if (is_output_disabled(ctx->param, count)) + continue; + event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; + if (event != MDP_GCE_NO_EVENT) + MM_REG_CLEAR(cmd, event); + } + + /* Enable the mutex */ + mtk_mutex_enable_by_cmdq(mutex[*mutex_id], (void *)&cmd->pkt); + + /* Wait SOF events and clear mutex modules (optional) */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + if (is_output_disabled(ctx->param, count)) + continue; + event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF]; + if (event != MDP_GCE_NO_EVENT) + MM_REG_WAIT(cmd, event); + } + + return 0; +} + +static int mdp_path_ctx_init(struct mdp_dev *mdp, struct mdp_path *path) +{ + const struct img_config *config = path->config; + int index, ret; + + if (config->num_components < 1) + return -EINVAL; + + for (index = 0; index < config->num_components; index++) { + ret = mdp_comp_ctx_config(mdp, &path->comps[index], + &config->components[index], + path->param); + if (ret) + return ret; + } + + return 0; +} + +static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, + struct mdp_path *path, u32 count) +{ + const struct img_config *config = path->config; + const struct img_mmsys_ctrl *ctrl = &config->ctrls[count]; + const struct img_mux *set; + struct mdp_comp_ctx *ctx; + s32 mutex_id; + int index, ret; + + /* Acquire components */ + ret = mdp_path_subfrm_require(path, cmd, &mutex_id, count); + if (ret) + return ret; + /* Enable mux settings */ + for (index = 0; index < ctrl->num_sets; index++) { + set = &ctrl->sets[index]; + cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, + set->value, 0xFFFFFFFF); + } + /* Config sub-frame information */ + for (index = (config->num_components - 1); index >= 0; index--) { + ctx = &path->comps[index]; + if (is_output_disabled(ctx->param, count)) + continue; + ret = call_op(ctx, config_subfrm, cmd, count); + if (ret) + return ret; + } + /* Run components */ + ret = mdp_path_subfrm_run(path, cmd, &mutex_id, count); + if (ret) + return ret; + /* Wait components done */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + if (is_output_disabled(ctx->param, count)) + continue; + ret = call_op(ctx, wait_comp_event, cmd); + if (ret) + return ret; + } + /* Advance to the next sub-frame */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + ret = call_op(ctx, advance_subfrm, cmd, count); + if (ret) + return ret; + } + /* Disable mux settings */ + for (index = 0; index < ctrl->num_sets; index++) { + set = &ctrl->sets[index]; + cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, + 0, 0xFFFFFFFF); + } + + return 0; +} + +static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, + struct mdp_path *path) +{ + const struct img_config *config = path->config; + struct mdp_comp_ctx *ctx; + int index, count, ret; + + /* Config path frame */ + /* Reset components */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + ret = call_op(ctx, init_comp, cmd); + if (ret) + return ret; + } + /* Config frame mode */ + for (index = 0; index < config->num_components; index++) { + const struct v4l2_rect *compose = + path->composes[ctx->param->outputs[0]]; + + ctx = &path->comps[index]; + ret = call_op(ctx, config_frame, cmd, compose); + if (ret) + return ret; + } + + /* Config path sub-frames */ + for (count = 0; count < config->num_subfrms; count++) { + ret = mdp_path_config_subfrm(cmd, path, count); + if (ret) + return ret; + } + /* Post processing information */ + for (index = 0; index < config->num_components; index++) { + ctx = &path->comps[index]; + ret = call_op(ctx, post_process, cmd); + if (ret) + return ret; + } + return 0; +} + +static int mdp_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, + size_t size) +{ + struct device *dev; + dma_addr_t dma_addr; + + pkt->va_base = kzalloc(size, GFP_KERNEL); + if (!pkt->va_base) { + kfree(pkt); + return -ENOMEM; + } + pkt->buf_size = size; + pkt->cl = (void *)client; + + dev = client->chan->mbox->dev; + dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); + kfree(pkt->va_base); + return -ENOMEM; + } + + pkt->pa_base = dma_addr; + + return 0; +} + +static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt) +{ + struct cmdq_client *client = (struct cmdq_client *)pkt->cl; + + dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, + DMA_TO_DEVICE); + kfree(pkt->va_base); + pkt->va_base = NULL; +} + +static void mdp_auto_release_work(struct work_struct *work) +{ + struct mdp_cmdq_cmd *cmd; + struct mdp_dev *mdp; + + cmd = container_of(work, struct mdp_cmdq_cmd, auto_release_work); + mdp = cmd->mdp; + + mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, + cmd->num_comps); + + atomic_dec(&mdp->job_count); + wake_up(&mdp->callback_wq); + + mdp_cmdq_pkt_destroy(&cmd->pkt); + kfree(cmd->comps); + cmd->comps = NULL; + kfree(cmd); + cmd = NULL; +} + +static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) +{ + struct mdp_cmdq_cmd *cmd; + struct cmdq_cb_data *data; + struct mdp_dev *mdp; + struct device *dev; + + if (!mssg) { + pr_info("%s:no callback data\n", __func__); + return; + } + + data = (struct cmdq_cb_data *)mssg; + cmd = container_of(data->pkt, struct mdp_cmdq_cmd, pkt); + mdp = cmd->mdp; + dev = &mdp->pdev->dev; + + if (cmd->mdp_ctx) + mdp_m2m_job_finish(cmd->mdp_ctx); + + if (cmd->user_cmdq_cb) { + struct cmdq_cb_data user_cb_data; + + user_cb_data.sta = data->sta; + user_cb_data.pkt = data->pkt; + cmd->user_cmdq_cb(user_cb_data); + } + + INIT_WORK(&cmd->auto_release_work, mdp_auto_release_work); + if (!queue_work(mdp->clock_wq, &cmd->auto_release_work)) { + dev_err(dev, "%s:queue_work fail!\n", __func__); + mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, + cmd->num_comps); + + atomic_dec(&mdp->job_count); + wake_up(&mdp->callback_wq); + + mdp_cmdq_pkt_destroy(&cmd->pkt); + kfree(cmd->comps); + cmd->comps = NULL; + kfree(cmd); + cmd = NULL; + } +} + +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param) +{ + struct mdp_path *path = NULL; + struct mdp_cmdq_cmd *cmd = NULL; + struct mdp_comp *comps = NULL; + struct device *dev = &mdp->pdev->dev; + int i, ret; + + atomic_inc(&mdp->job_count); + if (atomic_read(&mdp->suspended)) { + atomic_dec(&mdp->job_count); + return -ECANCELED; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto err_cmdq_data; + } + + if (mdp_cmdq_pkt_create(mdp->cmdq_clt, &cmd->pkt, SZ_16K)) { + ret = -ENOMEM; + goto err_cmdq_data; + } + + comps = kcalloc(param->config->num_components, sizeof(*comps), + GFP_KERNEL); + if (!comps) { + ret = -ENOMEM; + goto err_cmdq_data; + } + + path = kzalloc(sizeof(*path), GFP_KERNEL); + if (!path) { + ret = -ENOMEM; + goto err_cmdq_data; + } + + path->mdp_dev = mdp; + path->config = param->config; + path->param = param->param; + for (i = 0; i < param->param->num_outputs; i++) { + path->bounds[i].left = 0; + path->bounds[i].top = 0; + path->bounds[i].width = + param->param->outputs[i].buffer.format.width; + path->bounds[i].height = + param->param->outputs[i].buffer.format.height; + path->composes[i] = param->composes[i] ? + param->composes[i] : &path->bounds[i]; + } + + ret = mdp_path_ctx_init(mdp, path); + if (ret) { + dev_err(dev, "mdp_path_ctx_init error\n"); + goto err_cmdq_data; + } + + mtk_mutex_prepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + + ret = mdp_path_config(mdp, cmd, path); + if (ret) { + dev_err(dev, "mdp_path_config error\n"); + goto err_cmdq_data; + } + cmdq_pkt_finalize(&cmd->pkt); + + for (i = 0; i < param->config->num_components; i++) + memcpy(&comps[i], path->comps[i].comp, + sizeof(struct mdp_comp)); + + mdp->cmdq_clt->client.rx_callback = mdp_handle_cmdq_callback; + cmd->mdp = mdp; + cmd->user_cmdq_cb = param->cmdq_cb; + cmd->user_cb_data = param->cb_data; + cmd->comps = comps; + cmd->num_comps = param->config->num_components; + cmd->mdp_ctx = param->mdp_ctx; + + ret = mdp_comp_clocks_on(&mdp->pdev->dev, cmd->comps, cmd->num_comps); + if (ret) { + dev_err(dev, "comp %d failed to enable clock!\n", ret); + goto err_clock_off; + } + + dma_sync_single_for_device(mdp->cmdq_clt->chan->mbox->dev, + cmd->pkt.pa_base, cmd->pkt.cmd_buf_size, + DMA_TO_DEVICE); + ret = mbox_send_message(mdp->cmdq_clt->chan, &cmd->pkt); + if (ret < 0) { + dev_err(dev, "mbox send message fail %d!\n", ret); + goto err_clock_off; + } + mbox_client_txdone(mdp->cmdq_clt->chan, 0); + + kfree(path); + return 0; + +err_clock_off: + mtk_mutex_unprepare(mdp->mdp_mutex[MDP_PIPE_RDMA0]); + mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps, + cmd->num_comps); +err_cmdq_data: + kfree(path); + atomic_dec(&mdp->job_count); + wake_up(&mdp->callback_wq); + if (cmd->pkt.buf_size > 0) + mdp_cmdq_pkt_destroy(&cmd->pkt); + kfree(comps); + kfree(cmd); + return ret; +} +EXPORT_SYMBOL_GPL(mdp_cmdq_send); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h new file mode 100644 index 000000000000..43475b862ddb --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_CMDQ_H__ +#define __MTK_MDP3_CMDQ_H__ + +#include +#include +#include +#include "mtk-img-ipi.h" + +struct platform_device *mdp_get_plat_device(struct platform_device *pdev); + +struct mdp_cmdq_param { + struct img_config *config; + struct img_ipi_frameparam *param; + const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS]; + + void (*cmdq_cb)(struct cmdq_cb_data data); + void *cb_data; + void *mdp_ctx; +}; + +struct mdp_cmdq_cmd { + struct work_struct auto_release_work; + struct cmdq_pkt pkt; + s32 *event; + struct mdp_dev *mdp; + void (*user_cmdq_cb)(struct cmdq_cb_data data); + void *user_cb_data; + struct mdp_comp *comps; + void *mdp_ctx; + u8 num_comps; +}; + +struct mdp_dev; + +int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param); + +#endif /* __MTK_MDP3_CMDQ_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c new file mode 100644 index 000000000000..e62abf3587bf --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c @@ -0,0 +1,1033 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include +#include +#include "mtk-mdp3-comp.h" +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-regs.h" + +#include "mdp_reg_rdma.h" +#include "mdp_reg_ccorr.h" +#include "mdp_reg_rsz.h" +#include "mdp_reg_wrot.h" +#include "mdp_reg_wdma.h" + +static u32 mdp_comp_alias_id[MDP_COMP_TYPE_COUNT]; + +static inline const struct mdp_platform_config * +__get_plat_cfg(const struct mdp_comp_ctx *ctx) +{ + if (!ctx) + return NULL; + + return ctx->comp->mdp_dev->mdp_data->mdp_cfg; +} + +static s64 get_comp_flag(const struct mdp_comp_ctx *ctx) +{ + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + + if (mdp_cfg && mdp_cfg->rdma_rsz1_sram_sharing) + if (ctx->comp->id == MDP_COMP_RDMA0) + return BIT(MDP_COMP_RDMA0) | BIT(MDP_COMP_RSZ1); + + return BIT(ctx->comp->id); +} + +static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if (mdp_cfg && mdp_cfg->rdma_support_10bit) { + struct mdp_comp *prz1 = ctx->comp->mdp_dev->comp[MDP_COMP_RSZ1]; + + /* Disable RSZ1 */ + if (ctx->comp->id == MDP_COMP_RDMA0 && prz1) + MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE, + 0x0, BIT(0)); + } + + /* Reset RDMA */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0)); + MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8)); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0)); + return 0; +} + +static int config_rdma_frame(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, + const struct v4l2_rect *compose) +{ + const struct mdp_rdma_data *rdma = &ctx->param->rdma; + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + u32 colorformat = ctx->input->buffer.format.colorformat; + bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat); + bool en_ufo = MDP_COLOR_IS_UFP(colorformat); + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if (mdp_cfg && mdp_cfg->rdma_support_10bit) { + if (block10bit) + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7); + else + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7); + } + + /* Setup smi control */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON, + (7 << 4) + //burst type to 8 + (1 << 16), //enable pre-ultra + 0x00030071); + + /* Setup source frame info */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, rdma->src_ctrl, + 0x03C8FE0F); + + if (mdp_cfg) + if (mdp_cfg->rdma_support_10bit && en_ufo) { + /* Setup source buffer base */ + MM_REG_WRITE(cmd, subsys_id, + base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, + rdma->ufo_dec_y, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, + base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C, + rdma->ufo_dec_c, 0xFFFFFFFF); + /* Set 10bit source frame pitch */ + if (block10bit) + MM_REG_WRITE(cmd, subsys_id, + base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL, + rdma->mf_bkgd_in_pxl, 0x001FFFFF); + } + + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, rdma->control, + 0x1110); + /* Setup source buffer base */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, rdma->iova[0], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, rdma->iova[1], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, rdma->iova[2], + 0xFFFFFFFF); + /* Setup source buffer end */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, + rdma->iova_end[0], 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, + rdma->iova_end[1], 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, + rdma->iova_end[2], 0xFFFFFFFF); + /* Setup source frame pitch */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, + rdma->mf_bkgd, 0x001FFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, + rdma->sf_bkgd, 0x001FFFFF); + /* Setup color transform */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, + rdma->transform, 0x0F110000); + + return 0; +} + +static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct mdp_rdma_subfrm *subfrm = &ctx->param->rdma.subfrms[index]; + const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + u32 colorformat = ctx->input->buffer.format.colorformat; + bool block10bit = MDP_COLOR_IS_10BIT_PACKED(colorformat); + bool en_ufo = MDP_COLOR_IS_UFP(colorformat); + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Enable RDMA */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0)); + + /* Set Y pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, + subfrm->offset[0], 0xFFFFFFFF); + + /* Set 10bit UFO mode */ + if (mdp_cfg) + if (mdp_cfg->rdma_support_10bit && block10bit && en_ufo) + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_SRC_OFFSET_0_P, + subfrm->offset_0_p, 0xFFFFFFFF); + + /* Set U pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, + subfrm->offset[1], 0xFFFFFFFF); + /* Set V pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, + subfrm->offset[2], 0xFFFFFFFF); + /* Set source size */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, subfrm->src, + 0x1FFF1FFF); + /* Set target size */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, + subfrm->clip, 0x1FFF1FFF); + /* Set crop offset */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, + subfrm->clip_ofst, 0x003F001F); + + if (mdp_cfg && mdp_cfg->rdma_upsample_repeat_only) + if ((csf->in.right - csf->in.left + 1) > 320) + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); + + return 0; +} + +static int wait_rdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + struct device *dev = &ctx->comp->mdp_dev->pdev->dev; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if (ctx->comp->alias_id == 0) + MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); + else + dev_err(dev, "Do not support RDMA1_DONE event\n"); + + /* Disable RDMA */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0)); + return 0; +} + +static const struct mdp_comp_ops rdma_ops = { + .get_comp_flag = get_comp_flag, + .init_comp = init_rdma, + .config_frame = config_rdma_frame, + .config_subfrm = config_rdma_subfrm, + .wait_comp_event = wait_rdma_event, +}; + +static int init_rsz(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Reset RSZ */ + MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16)); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16)); + /* Enable RSZ */ + MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0)); + return 0; +} + +static int config_rsz_frame(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, + const struct v4l2_rect *compose) +{ + const struct mdp_rsz_data *rsz = &ctx->param->rsz; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if (ctx->param->frame.bypass) { + /* Disable RSZ */ + MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); + return 0; + } + + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, rsz->control1, + 0x03FFFDF3); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, rsz->control2, + 0x0FFFC290); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, + rsz->coeff_step_x, 0x007FFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, + rsz->coeff_step_y, 0x007FFFFF); + return 0; +} + +static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct mdp_rsz_subfrm *subfrm = &ctx->param->rsz.subfrms[index]; + const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, subfrm->control2, + 0x00003800); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, subfrm->src, + 0xFFFFFFFF); + + if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) + if ((csf->in.right - csf->in.left + 1) <= 16) + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, + BIT(27), BIT(27)); + + MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, + csf->luma.left, 0xFFFF); + MM_REG_WRITE(cmd, subsys_id, + base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, + csf->luma.left_subpix, 0x1FFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, + csf->luma.top, 0xFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, + csf->luma.top_subpix, 0x1FFFFF); + MM_REG_WRITE(cmd, subsys_id, + base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, + csf->chroma.left, 0xFFFF); + MM_REG_WRITE(cmd, subsys_id, + base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, + csf->chroma.left_subpix, 0x1FFFFF); + + MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, subfrm->clip, + 0xFFFFFFFF); + + return 0; +} + +static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + + if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) { + const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if ((csf->in.right - csf->in.left + 1) <= 16) + MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, + BIT(27)); + } + + return 0; +} + +static const struct mdp_comp_ops rsz_ops = { + .get_comp_flag = get_comp_flag, + .init_comp = init_rsz, + .config_frame = config_rsz_frame, + .config_subfrm = config_rsz_subfrm, + .advance_subfrm = advance_rsz_subfrm, +}; + +static int init_wrot(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Reset WROT */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0)); + MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0)); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0)); + MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0)); + return 0; +} + +static int config_wrot_frame(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, + const struct v4l2_rect *compose) +{ + const struct mdp_wrot_data *wrot = &ctx->param->wrot; + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Write frame base address */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, wrot->iova[0], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, wrot->iova[1], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, wrot->iova[2], + 0xFFFFFFFF); + /* Write frame related registers */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, wrot->control, + 0xF131510F); + /* Write frame Y pitch */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, wrot->stride[0], + 0x0000FFFF); + /* Write frame UV pitch */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, wrot->stride[1], + 0xFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, wrot->stride[2], + 0xFFFF); + /* Write matrix control */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, wrot->mat_ctrl, 0xF3); + + /* Set the fixed ALPHA as 0xFF */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000, + 0xFF000000); + /* Set VIDO_EOL_SEL */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); + /* Set VIDO_FIFO_TEST */ + if (wrot->fifo_test != 0) + MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST, + wrot->fifo_test, 0xFFF); + /* Filter enable */ + if (mdp_cfg && mdp_cfg->wrot_filter_constraint) + MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, + wrot->filter, 0x77); + + return 0; +} + +static int config_wrot_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct mdp_wrot_subfrm *subfrm = &ctx->param->wrot.subfrms[index]; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Write Y pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR, + subfrm->offset[0], 0x0FFFFFFF); + /* Write U pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C, + subfrm->offset[1], 0x0FFFFFFF); + /* Write V pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V, + subfrm->offset[2], 0x0FFFFFFF); + /* Write source size */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, subfrm->src, + 0x1FFF1FFF); + /* Write target size */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, subfrm->clip, + 0x1FFF1FFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, subfrm->clip_ofst, + 0x1FFF1FFF); + + MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, + subfrm->main_buf, 0x1FFF7F00); + + /* Enable WROT */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); + + return 0; +} + +static int wait_wrot_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + const struct mdp_platform_config *mdp_cfg = __get_plat_cfg(ctx); + struct device *dev = &ctx->comp->mdp_dev->pdev->dev; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + if (ctx->comp->alias_id == 0) + MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); + else + dev_err(dev, "Do not support WROT1_DONE event\n"); + + if (mdp_cfg && mdp_cfg->wrot_filter_constraint) + MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0, + 0x77); + + /* Disable WROT */ + MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0)); + + return 0; +} + +static const struct mdp_comp_ops wrot_ops = { + .get_comp_flag = get_comp_flag, + .init_comp = init_wrot, + .config_frame = config_wrot_frame, + .config_subfrm = config_wrot_subfrm, + .wait_comp_event = wait_wrot_event, +}; + +static int init_wdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Reset WDMA */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0)); + MM_REG_POLL(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0)); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0)); + return 0; +} + +static int config_wdma_frame(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, + const struct v4l2_rect *compose) +{ + const struct mdp_wdma_data *wdma = &ctx->param->wdma; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050, + 0xFFFFFFFF); + + /* Setup frame information */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, wdma->wdma_cfg, + 0x0F01B8F0); + /* Setup frame base address */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, wdma->iova[0], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, wdma->iova[1], + 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, wdma->iova[2], + 0xFFFFFFFF); + /* Setup Y pitch */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, + wdma->w_in_byte, 0x0000FFFF); + /* Setup UV pitch */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_UV_PITCH, + wdma->uv_stride, 0x0000FFFF); + /* Set the fixed ALPHA as 0xFF */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, + 0x800000FF); + + return 0; +} + +static int config_wdma_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct mdp_wdma_subfrm *subfrm = &ctx->param->wdma.subfrms[index]; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* Write Y pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, + subfrm->offset[0], 0x0FFFFFFF); + /* Write U pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, + subfrm->offset[1], 0x0FFFFFFF); + /* Write V pixel offset */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, + subfrm->offset[2], 0x0FFFFFFF); + /* Write source size */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, subfrm->src, + 0x3FFF3FFF); + /* Write target size */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, subfrm->clip, + 0x3FFF3FFF); + /* Write clip offset */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, subfrm->clip_ofst, + 0x3FFF3FFF); + + /* Enable WDMA */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0)); + + return 0; +} + +static int wait_wdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); + /* Disable WDMA */ + MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0)); + return 0; +} + +static const struct mdp_comp_ops wdma_ops = { + .get_comp_flag = get_comp_flag, + .init_comp = init_wdma, + .config_frame = config_wdma_frame, + .config_subfrm = config_wdma_subfrm, + .wait_comp_event = wait_wdma_event, +}; + +static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) +{ + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + + /* CCORR enable */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0)); + /* Relay mode */ + MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0)); + return 0; +} + +static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index) +{ + const struct img_comp_subfrm *csf = &ctx->param->subfrms[index]; + phys_addr_t base = ctx->comp->reg_base; + u8 subsys_id = ctx->comp->subsys_id; + u32 hsize, vsize; + + hsize = csf->in.right - csf->in.left + 1; + vsize = csf->in.bottom - csf->in.top + 1; + MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_SIZE, + (hsize << 16) + (vsize << 0), 0x1FFF1FFF); + return 0; +} + +static const struct mdp_comp_ops ccorr_ops = { + .get_comp_flag = get_comp_flag, + .init_comp = init_ccorr, + .config_subfrm = config_ccorr_subfrm, +}; + +static const struct mdp_comp_ops *mdp_comp_ops[MDP_COMP_TYPE_COUNT] = { + [MDP_COMP_TYPE_RDMA] = &rdma_ops, + [MDP_COMP_TYPE_RSZ] = &rsz_ops, + [MDP_COMP_TYPE_WROT] = &wrot_ops, + [MDP_COMP_TYPE_WDMA] = &wdma_ops, + [MDP_COMP_TYPE_CCORR] = &ccorr_ops, +}; + +struct mdp_comp_match { + enum mdp_comp_type type; + u32 alias_id; +}; + +static const struct mdp_comp_match mdp_comp_matches[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_WPEI] = { MDP_COMP_TYPE_WPEI, 0 }, + [MDP_COMP_WPEO] = { MDP_COMP_TYPE_EXTO, 2 }, + [MDP_COMP_WPEI2] = { MDP_COMP_TYPE_WPEI, 1 }, + [MDP_COMP_WPEO2] = { MDP_COMP_TYPE_EXTO, 3 }, + [MDP_COMP_ISP_IMGI] = { MDP_COMP_TYPE_IMGI, 0 }, + [MDP_COMP_ISP_IMGO] = { MDP_COMP_TYPE_EXTO, 0 }, + [MDP_COMP_ISP_IMG2O] = { MDP_COMP_TYPE_EXTO, 1 }, + + [MDP_COMP_CAMIN] = { MDP_COMP_TYPE_DL_PATH, 0 }, + [MDP_COMP_CAMIN2] = { MDP_COMP_TYPE_DL_PATH, 1 }, + [MDP_COMP_RDMA0] = { MDP_COMP_TYPE_RDMA, 0 }, + [MDP_COMP_CCORR0] = { MDP_COMP_TYPE_CCORR, 0 }, + [MDP_COMP_RSZ0] = { MDP_COMP_TYPE_RSZ, 0 }, + [MDP_COMP_RSZ1] = { MDP_COMP_TYPE_RSZ, 1 }, + [MDP_COMP_PATH0_SOUT] = { MDP_COMP_TYPE_PATH, 0 }, + [MDP_COMP_PATH1_SOUT] = { MDP_COMP_TYPE_PATH, 1 }, + [MDP_COMP_WROT0] = { MDP_COMP_TYPE_WROT, 0 }, + [MDP_COMP_WDMA] = { MDP_COMP_TYPE_WDMA, 0 }, +}; + +static const struct of_device_id mdp_comp_dt_ids[] = { + { + .compatible = "mediatek,mt8183-mdp3-rdma", + .data = (void *)MDP_COMP_TYPE_RDMA, + }, { + .compatible = "mediatek,mt8183-mdp3-ccorr", + .data = (void *)MDP_COMP_TYPE_CCORR, + }, { + .compatible = "mediatek,mt8183-mdp3-rsz", + .data = (void *)MDP_COMP_TYPE_RSZ, + }, { + .compatible = "mediatek,mt8183-mdp3-wrot", + .data = (void *)MDP_COMP_TYPE_WROT, + }, { + .compatible = "mediatek,mt8183-mdp3-wdma", + .data = (void *)MDP_COMP_TYPE_WDMA, + }, + {} +}; + +static const struct of_device_id mdp_sub_comp_dt_ids[] = { + { + .compatible = "mediatek,mt8183-mdp3-wdma", + .data = (void *)MDP_COMP_TYPE_PATH, + }, { + .compatible = "mediatek,mt8183-mdp3-wrot", + .data = (void *)MDP_COMP_TYPE_PATH, + }, + {} +}; + +/* Used to describe the item order in MDP property */ +struct mdp_comp_info { + u32 clk_num; + u32 clk_ofst; + u32 dts_reg_ofst; +}; + +static const struct mdp_comp_info mdp_comp_dt_info[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = {2, 0, 0}, + [MDP_COMP_RSZ0] = {1, 0, 0}, + [MDP_COMP_WROT0] = {1, 0, 0}, + [MDP_COMP_WDMA] = {1, 0, 0}, + [MDP_COMP_CCORR0] = {1, 0, 0}, +}; + +static inline bool is_dma_capable(const enum mdp_comp_type type) +{ + return (type == MDP_COMP_TYPE_RDMA || + type == MDP_COMP_TYPE_WROT || + type == MDP_COMP_TYPE_WDMA); +} + +static inline bool is_bypass_gce_event(const enum mdp_comp_type type) +{ + /* + * Subcomponent PATH is only used for the direction of data flow and + * dose not need to wait for GCE event. + */ + return (type == MDP_COMP_TYPE_PATH); +} + +static int mdp_comp_get_id(enum mdp_comp_type type, int alias_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mdp_comp_matches); i++) + if (mdp_comp_matches[i].type == type && + mdp_comp_matches[i].alias_id == alias_id) + return i; + return -ENODEV; +} + +int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp) +{ + int i, ret; + + if (comp->comp_dev) { + ret = pm_runtime_get_sync(comp->comp_dev); + if (ret < 0) { + dev_err(dev, + "Failed to get power, err %d. type:%d id:%d\n", + ret, comp->type, comp->id); + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(comp->clks); i++) { + if (IS_ERR_OR_NULL(comp->clks[i])) + continue; + ret = clk_prepare_enable(comp->clks[i]); + if (ret) { + dev_err(dev, + "Failed to enable clk %d. type:%d id:%d\n", + i, comp->type, comp->id); + return ret; + } + } + + return 0; +} + +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(comp->clks); i++) { + if (IS_ERR_OR_NULL(comp->clks[i])) + continue; + clk_disable_unprepare(comp->clks[i]); + } + + if (comp->comp_dev) + pm_runtime_put(comp->comp_dev); +} + +int mdp_comp_clocks_on(struct device *dev, struct mdp_comp *comps, int num) +{ + int i; + + for (i = 0; i < num; i++) + if (mdp_comp_clock_on(dev, &comps[i]) != 0) + return ++i; + + return 0; +} + +void mdp_comp_clocks_off(struct device *dev, struct mdp_comp *comps, int num) +{ + int i; + + for (i = 0; i < num; i++) + mdp_comp_clock_off(dev, &comps[i]); +} + +static int mdp_get_subsys_id(struct device *dev, struct device_node *node, + struct mdp_comp *comp) +{ + struct platform_device *comp_pdev; + struct cmdq_client_reg cmdq_reg; + int ret = 0; + int index = 0; + + if (!dev || !node || !comp) + return -EINVAL; + + comp_pdev = of_find_device_by_node(node); + + if (!comp_pdev) { + dev_err(dev, "get comp_pdev fail! comp id=%d type=%d\n", + comp->id, comp->type); + return -ENODEV; + } + + index = mdp_comp_dt_info[comp->id].dts_reg_ofst; + ret = cmdq_dev_get_client_reg(&comp_pdev->dev, &cmdq_reg, index); + if (ret != 0) { + dev_err(&comp_pdev->dev, "cmdq_dev_get_subsys fail!\n"); + return -EINVAL; + } + + comp->subsys_id = cmdq_reg.subsys; + dev_dbg(&comp_pdev->dev, "subsys id=%d\n", cmdq_reg.subsys); + + return 0; +} + +static void __mdp_comp_init(struct mdp_dev *mdp, struct device_node *node, + struct mdp_comp *comp) +{ + struct resource res; + phys_addr_t base; + int index = mdp_comp_dt_info[comp->id].dts_reg_ofst; + + if (of_address_to_resource(node, index, &res) < 0) + base = 0L; + else + base = res.start; + + comp->mdp_dev = mdp; + comp->regs = of_iomap(node, 0); + comp->reg_base = base; +} + +static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node, + struct mdp_comp *comp, enum mtk_mdp_comp_id id) +{ + struct device *dev = &mdp->pdev->dev; + int clk_num; + int clk_ofst; + int i; + s32 event; + + if (id < 0 || id >= MDP_MAX_COMP_COUNT) { + dev_err(dev, "Invalid component id %d\n", id); + return -EINVAL; + } + + comp->id = id; + comp->type = mdp_comp_matches[id].type; + comp->alias_id = mdp_comp_matches[id].alias_id; + comp->ops = mdp_comp_ops[comp->type]; + __mdp_comp_init(mdp, node, comp); + + clk_num = mdp_comp_dt_info[id].clk_num; + clk_ofst = mdp_comp_dt_info[id].clk_ofst; + + for (i = 0; i < clk_num; i++) { + comp->clks[i] = of_clk_get(node, i + clk_ofst); + if (IS_ERR(comp->clks[i])) + break; + } + + mdp_get_subsys_id(dev, node, comp); + + /* Set GCE SOF event */ + if (is_bypass_gce_event(comp->type) || + of_property_read_u32_index(node, "mediatek,gce-events", + MDP_GCE_EVENT_SOF, &event)) + event = MDP_GCE_NO_EVENT; + + comp->gce_event[MDP_GCE_EVENT_SOF] = event; + + /* Set GCE EOF event */ + if (is_dma_capable(comp->type)) { + if (of_property_read_u32_index(node, "mediatek,gce-events", + MDP_GCE_EVENT_EOF, &event)) { + dev_err(dev, "Component id %d has no EOF\n", id); + return -EINVAL; + } + } else { + event = MDP_GCE_NO_EVENT; + } + + comp->gce_event[MDP_GCE_EVENT_EOF] = event; + + return 0; +} + +static void mdp_comp_deinit(struct mdp_comp *comp) +{ + if (!comp) + return; + + if (comp->regs) + iounmap(comp->regs); +} + +static struct mdp_comp *mdp_comp_create(struct mdp_dev *mdp, + struct device_node *node, + enum mtk_mdp_comp_id id) +{ + struct device *dev = &mdp->pdev->dev; + struct mdp_comp *comp; + int ret; + + if (mdp->comp[id]) + return ERR_PTR(-EEXIST); + + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); + if (!comp) + return ERR_PTR(-ENOMEM); + + ret = mdp_comp_init(mdp, node, comp, id); + if (ret) { + kfree(comp); + return ERR_PTR(ret); + } + mdp->comp[id] = comp; + mdp->comp[id]->mdp_dev = mdp; + + dev_dbg(dev, "%s type:%d alias:%d id:%d base:%#x regs:%p\n", + dev->of_node->name, comp->type, comp->alias_id, id, + (u32)comp->reg_base, comp->regs); + return comp; +} + +static int mdp_comp_sub_create(struct mdp_dev *mdp) +{ + struct device *dev = &mdp->pdev->dev; + struct device_node *node, *parent; + + parent = dev->of_node->parent; + + for_each_child_of_node(parent, node) { + const struct of_device_id *of_id; + enum mdp_comp_type type; + int id, alias_id; + struct mdp_comp *comp; + + of_id = of_match_node(mdp_sub_comp_dt_ids, node); + if (!of_id) + continue; + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled sub comp. %pOF\n", + node); + continue; + } + + type = (enum mdp_comp_type)(uintptr_t)of_id->data; + alias_id = mdp_comp_alias_id[type]; + id = mdp_comp_get_id(type, alias_id); + if (id < 0) { + dev_err(dev, + "Fail to get sub comp. id: type %d alias %d\n", + type, alias_id); + return -EINVAL; + } + mdp_comp_alias_id[type]++; + + comp = mdp_comp_create(mdp, node, id); + if (IS_ERR(comp)) + return PTR_ERR(comp); + } + + return 0; +} + +void mdp_comp_destroy(struct mdp_dev *mdp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) { + if (mdp->comp[i]) { + pm_runtime_disable(mdp->comp[i]->comp_dev); + mdp_comp_deinit(mdp->comp[i]); + kfree(mdp->comp[i]); + mdp->comp[i] = NULL; + } + } +} + +int mdp_comp_config(struct mdp_dev *mdp) +{ + struct device *dev = &mdp->pdev->dev; + struct device_node *node, *parent; + struct platform_device *pdev; + int ret; + + memset(mdp_comp_alias_id, 0, sizeof(mdp_comp_alias_id)); + + parent = dev->of_node->parent; + /* Iterate over sibling MDP function blocks */ + for_each_child_of_node(parent, node) { + const struct of_device_id *of_id; + enum mdp_comp_type type; + int id, alias_id; + struct mdp_comp *comp; + + of_id = of_match_node(mdp_comp_dt_ids, node); + if (!of_id) + continue; + + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled component %pOF\n", + node); + continue; + } + + type = (enum mdp_comp_type)(uintptr_t)of_id->data; + alias_id = mdp_comp_alias_id[type]; + id = mdp_comp_get_id(type, alias_id); + if (id < 0) { + dev_err(dev, + "Fail to get component id: type %d alias %d\n", + type, alias_id); + continue; + } + mdp_comp_alias_id[type]++; + + comp = mdp_comp_create(mdp, node, id); + if (IS_ERR(comp)) { + ret = PTR_ERR(comp); + goto err_init_comps; + } + + /* Only DMA capable components need the pm control */ + comp->comp_dev = NULL; + if (!is_dma_capable(comp->type)) + continue; + + pdev = of_find_device_by_node(node); + if (!pdev) { + dev_warn(dev, "can't find platform device of node:%s\n", + node->name); + return -ENODEV; + } + + comp->comp_dev = &pdev->dev; + pm_runtime_enable(comp->comp_dev); + } + + ret = mdp_comp_sub_create(mdp); + if (ret) + goto err_init_comps; + + return 0; + +err_init_comps: + mdp_comp_destroy(mdp); + return ret; +} + +int mdp_comp_ctx_config(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx, + const struct img_compparam *param, + const struct img_ipi_frameparam *frame) +{ + struct device *dev = &mdp->pdev->dev; + int i; + + if (param->type < 0 || param->type >= MDP_MAX_COMP_COUNT) { + dev_err(dev, "Invalid component id %d", param->type); + return -EINVAL; + } + + ctx->comp = mdp->comp[param->type]; + if (!ctx->comp) { + dev_err(dev, "Uninit component id %d", param->type); + return -EINVAL; + } + + ctx->param = param; + ctx->input = &frame->inputs[param->input]; + for (i = 0; i < param->num_outputs; i++) + ctx->outputs[i] = &frame->outputs[param->outputs[i]]; + return 0; +} diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h new file mode 100644 index 000000000000..dc48f55ac4f7 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_COMP_H__ +#define __MTK_MDP3_COMP_H__ + +#include "mtk-mdp3-cmdq.h" + +#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask, ...) \ + cmdq_pkt_write_mask(&((cmd)->pkt), id, \ + (base) + (ofst), (val), (mask), ##__VA_ARGS__) + +#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \ +do { \ + typeof(mask) (m) = (mask); \ + MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \ + (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ + (0xffffffff) : (m), ##__VA_ARGS__); \ +} while (0) + +#define MM_REG_WAIT(cmd, evt) \ +do { \ + typeof(cmd) (c) = (cmd); \ + typeof(evt) (e) = (evt); \ + cmdq_pkt_wfe(&((c)->pkt), (e), true); \ +} while (0) + +#define MM_REG_WAIT_NO_CLEAR(cmd, evt) \ +do { \ + typeof(cmd) (c) = (cmd); \ + typeof(evt) (e) = (evt); \ + cmdq_pkt_wfe(&((c)->pkt), (e), false); \ +} while (0) + +#define MM_REG_CLEAR(cmd, evt) \ +do { \ + typeof(cmd) (c) = (cmd); \ + typeof(evt) (e) = (evt); \ + cmdq_pkt_clear_event(&((c)->pkt), (e)); \ +} while (0) + +#define MM_REG_SET_EVENT(cmd, evt) \ +do { \ + typeof(cmd) (c) = (cmd); \ + typeof(evt) (e) = (evt); \ + cmdq_pkt_set_event(&((c)->pkt), (e)); \ +} while (0) + +#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask, ...) \ +do { \ + typeof(_mask) (_m) = (_mask); \ + cmdq_pkt_poll_mask(&((cmd)->pkt), id, \ + (base) + (ofst), (val), (_m), ##__VA_ARGS__); \ +} while (0) + +#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \ +do { \ + typeof(mask) (m) = (mask); \ + MM_REG_POLL_MASK((cmd), id, base, ofst, val, \ + (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ + (0xffffffff) : (m), ##__VA_ARGS__); \ +} while (0) + +enum mtk_mdp_comp_id { + MDP_COMP_NONE = -1, /* Invalid engine */ + + /* ISP */ + MDP_COMP_WPEI = 0, + MDP_COMP_WPEO, /* 1 */ + MDP_COMP_WPEI2, /* 2 */ + MDP_COMP_WPEO2, /* 3 */ + MDP_COMP_ISP_IMGI, /* 4 */ + MDP_COMP_ISP_IMGO, /* 5 */ + MDP_COMP_ISP_IMG2O, /* 6 */ + + /* IPU */ + MDP_COMP_IPUI, /* 7 */ + MDP_COMP_IPUO, /* 8 */ + + /* MDP */ + MDP_COMP_CAMIN, /* 9 */ + MDP_COMP_CAMIN2, /* 10 */ + MDP_COMP_RDMA0, /* 11 */ + MDP_COMP_AAL0, /* 12 */ + MDP_COMP_CCORR0, /* 13 */ + MDP_COMP_RSZ0, /* 14 */ + MDP_COMP_RSZ1, /* 15 */ + MDP_COMP_TDSHP0, /* 16 */ + MDP_COMP_COLOR0, /* 17 */ + MDP_COMP_PATH0_SOUT, /* 18 */ + MDP_COMP_PATH1_SOUT, /* 19 */ + MDP_COMP_WROT0, /* 20 */ + MDP_COMP_WDMA, /* 21 */ + + /* Dummy Engine */ + MDP_COMP_RDMA1, /* 22 */ + MDP_COMP_RSZ2, /* 23 */ + MDP_COMP_TDSHP1, /* 24 */ + MDP_COMP_WROT1, /* 25 */ + + MDP_MAX_COMP_COUNT /* ALWAYS keep at the end */ +}; + +enum mdp_comp_type { + MDP_COMP_TYPE_INVALID = 0, + + MDP_COMP_TYPE_RDMA, + MDP_COMP_TYPE_RSZ, + MDP_COMP_TYPE_WROT, + MDP_COMP_TYPE_WDMA, + MDP_COMP_TYPE_PATH, + + MDP_COMP_TYPE_TDSHP, + MDP_COMP_TYPE_COLOR, + MDP_COMP_TYPE_DRE, + MDP_COMP_TYPE_CCORR, + MDP_COMP_TYPE_HDR, + + MDP_COMP_TYPE_IMGI, + MDP_COMP_TYPE_WPEI, + MDP_COMP_TYPE_EXTO, /* External path */ + MDP_COMP_TYPE_DL_PATH, /* Direct-link path */ + + MDP_COMP_TYPE_COUNT /* ALWAYS keep at the end */ +}; + +#define MDP_GCE_NO_EVENT (-1) +enum { + MDP_GCE_EVENT_SOF = 0, + MDP_GCE_EVENT_EOF = 1, + MDP_GCE_EVENT_MAX, +}; + +struct mdp_comp_ops; + +struct mdp_comp { + struct mdp_dev *mdp_dev; + void __iomem *regs; + phys_addr_t reg_base; + u8 subsys_id; + struct clk *clks[6]; + struct device *comp_dev; + enum mdp_comp_type type; + enum mtk_mdp_comp_id id; + u32 alias_id; + s32 gce_event[MDP_GCE_EVENT_MAX]; + const struct mdp_comp_ops *ops; +}; + +struct mdp_comp_ctx { + struct mdp_comp *comp; + const struct img_compparam *param; + const struct img_input *input; + const struct img_output *outputs[IMG_MAX_HW_OUTPUTS]; +}; + +struct mdp_comp_ops { + s64 (*get_comp_flag)(const struct mdp_comp_ctx *ctx); + int (*init_comp)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd); + int (*config_frame)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd, + const struct v4l2_rect *compose); + int (*config_subfrm)(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index); + int (*wait_comp_event)(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd); + int (*advance_subfrm)(struct mdp_comp_ctx *ctx, + struct mdp_cmdq_cmd *cmd, u32 index); + int (*post_process)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd); +}; + +struct mdp_dev; + +int mdp_comp_config(struct mdp_dev *mdp); +void mdp_comp_destroy(struct mdp_dev *mdp); +int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp); +void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp); +int mdp_comp_clocks_on(struct device *dev, struct mdp_comp *comps, int num); +void mdp_comp_clocks_off(struct device *dev, struct mdp_comp *comps, int num); +int mdp_comp_ctx_config(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx, + const struct img_compparam *param, + const struct img_ipi_frameparam *frame); + +#endif /* __MTK_MDP3_COMP_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c new file mode 100644 index 000000000000..cde59579b7ae --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-m2m.h" + +static const struct mdp_platform_config mt8183_plat_cfg = { + .rdma_support_10bit = true, + .rdma_rsz1_sram_sharing = true, + .rdma_upsample_repeat_only = true, + .rsz_disable_dcm_small_sample = false, + .wrot_filter_constraint = false, +}; + +static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { + [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8183-mmsys" }, + [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8183-disp-mutex" }, + [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } +}; + +static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, + [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, + [MDP_COMP_RSZ1] = MUTEX_MOD_IDX_MDP_RSZ1, + [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, + [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, + [MDP_COMP_WDMA] = MUTEX_MOD_IDX_MDP_WDMA, + [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, + [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, +}; + +static const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { + .mdp_probe_infra = mt8183_mdp_probe_infra, + .mdp_cfg = &mt8183_plat_cfg, + .mdp_mutex_table_idx = mt8183_mutex_idx, +}; + +static const struct of_device_id mdp_of_ids[] = { + { .compatible = "mediatek,mt8183-mdp3-rdma", + .data = &mt8183_mdp_driver_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mdp_of_ids); + +static struct platform_device *__get_pdev_by_id(struct platform_device *pdev, + enum mdp_infra_id id) +{ + struct device_node *node; + struct platform_device *mdp_pdev = NULL; + const struct mtk_mdp_driver_data *mdp_data; + const char *compat; + + if (!pdev) + return NULL; + + if (id < MDP_INFRA_MMSYS || id >= MDP_INFRA_MAX) { + dev_err(&pdev->dev, "Illegal infra id %d\n", id); + return NULL; + } + + mdp_data = of_device_get_match_data(&pdev->dev); + if (!mdp_data) { + dev_err(&pdev->dev, "have no driver data to find node\n"); + return NULL; + } + compat = mdp_data->mdp_probe_infra[id].compatible; + + node = of_find_compatible_node(NULL, NULL, compat); + if (WARN_ON(!node)) { + dev_err(&pdev->dev, "find node from id %d failed\n", id); + return NULL; + } + + mdp_pdev = of_find_device_by_node(node); + of_node_put(node); + if (WARN_ON(!mdp_pdev)) { + dev_err(&pdev->dev, "find pdev from id %d failed\n", id); + return NULL; + } + + return mdp_pdev; +} + +struct platform_device *mdp_get_plat_device(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *mdp_node; + struct platform_device *mdp_pdev; + + mdp_node = of_parse_phandle(dev->of_node, MDP_PHANDLE_NAME, 0); + if (!mdp_node) { + dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME); + return NULL; + } + + mdp_pdev = of_find_device_by_node(mdp_node); + of_node_put(mdp_node); + + return mdp_pdev; +} +EXPORT_SYMBOL_GPL(mdp_get_plat_device); + +int mdp_vpu_get_locked(struct mdp_dev *mdp) +{ + int ret = 0; + + if (mdp->vpu_count++ == 0) { + ret = rproc_boot(mdp->rproc_handle); + if (ret) { + dev_err(&mdp->pdev->dev, + "vpu_load_firmware failed %d\n", ret); + goto err_load_vpu; + } + ret = mdp_vpu_register(mdp); + if (ret) { + dev_err(&mdp->pdev->dev, + "mdp_vpu register failed %d\n", ret); + goto err_reg_vpu; + } + ret = mdp_vpu_dev_init(&mdp->vpu, mdp->scp, &mdp->vpu_lock); + if (ret) { + dev_err(&mdp->pdev->dev, + "mdp_vpu device init failed %d\n", ret); + goto err_init_vpu; + } + } + return 0; + +err_init_vpu: + mdp_vpu_unregister(mdp); +err_reg_vpu: +err_load_vpu: + mdp->vpu_count--; + return ret; +} + +void mdp_vpu_put_locked(struct mdp_dev *mdp) +{ + if (--mdp->vpu_count == 0) { + mdp_vpu_dev_deinit(&mdp->vpu); + mdp_vpu_unregister(mdp); + } +} + +void mdp_video_device_release(struct video_device *vdev) +{ + struct mdp_dev *mdp = (struct mdp_dev *)video_get_drvdata(vdev); + int i; + + scp_put(mdp->scp); + + destroy_workqueue(mdp->job_wq); + destroy_workqueue(mdp->clock_wq); + + pm_runtime_disable(&mdp->pdev->dev); + + vb2_dma_contig_clear_max_seg_size(&mdp->pdev->dev); + + mdp_comp_destroy(mdp); + for (i = 0; i < MDP_PIPE_MAX; i++) + mtk_mutex_put(mdp->mdp_mutex[i]); + + mdp_vpu_shared_mem_free(&mdp->vpu); + v4l2_m2m_release(mdp->m2m_dev); + kfree(mdp); +} + +static int mdp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mdp_dev *mdp; + struct platform_device *mm_pdev; + int ret, i; + + mdp = kzalloc(sizeof(*mdp), GFP_KERNEL); + if (!mdp) { + ret = -ENOMEM; + goto err_return; + } + + mdp->pdev = pdev; + mdp->mdp_data = of_device_get_match_data(&pdev->dev); + + mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MMSYS); + if (!mm_pdev) { + ret = -ENODEV; + goto err_return; + } + mdp->mdp_mmsys = &mm_pdev->dev; + + mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MUTEX); + if (WARN_ON(!mm_pdev)) { + ret = -ENODEV; + goto err_return; + } + for (i = 0; i < MDP_PIPE_MAX; i++) { + mdp->mdp_mutex[i] = mtk_mutex_get(&mm_pdev->dev); + if (!mdp->mdp_mutex[i]) { + ret = -ENODEV; + goto err_return; + } + } + + ret = mdp_comp_config(mdp); + if (ret) { + dev_err(dev, "Failed to config mdp components\n"); + goto err_return; + } + + mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 0); + if (!mdp->job_wq) { + dev_err(dev, "Unable to create job workqueue\n"); + ret = -ENOMEM; + goto err_deinit_comp; + } + + mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", WQ_FREEZABLE, + 0); + if (!mdp->clock_wq) { + dev_err(dev, "Unable to create clock workqueue\n"); + ret = -ENOMEM; + goto err_destroy_job_wq; + } + + mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP); + if (WARN_ON(!mm_pdev)) { + dev_err(&pdev->dev, "Could not get scp device\n"); + ret = -ENODEV; + goto err_destroy_clock_wq; + } + mdp->scp = platform_get_drvdata(mm_pdev); + mdp->rproc_handle = scp_get_rproc(mdp->scp); + dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle); + + mutex_init(&mdp->vpu_lock); + mutex_init(&mdp->m2m_lock); + + mdp->cmdq_clt = cmdq_mbox_create(dev, 0); + if (IS_ERR(mdp->cmdq_clt)) { + ret = PTR_ERR(mdp->cmdq_clt); + goto err_put_scp; + } + + init_waitqueue_head(&mdp->callback_wq); + ida_init(&mdp->mdp_ida); + platform_set_drvdata(pdev, mdp); + + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + + ret = v4l2_device_register(dev, &mdp->v4l2_dev); + if (ret) { + dev_err(dev, "Failed to register v4l2 device\n"); + ret = -EINVAL; + goto err_mbox_destroy; + } + + ret = mdp_m2m_device_register(mdp); + if (ret) { + v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n"); + goto err_unregister_device; + } + + dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id); + return 0; + +err_unregister_device: + v4l2_device_unregister(&mdp->v4l2_dev); +err_mbox_destroy: + cmdq_mbox_destroy(mdp->cmdq_clt); +err_put_scp: + scp_put(mdp->scp); +err_destroy_clock_wq: + destroy_workqueue(mdp->clock_wq); +err_destroy_job_wq: + destroy_workqueue(mdp->job_wq); +err_deinit_comp: + mdp_comp_destroy(mdp); +err_return: + for (i = 0; i < MDP_PIPE_MAX; i++) + mtk_mutex_put(mdp->mdp_mutex[i]); + kfree(mdp); + dev_dbg(dev, "Errno %d\n", ret); + return ret; +} + +static int mdp_remove(struct platform_device *pdev) +{ + struct mdp_dev *mdp = platform_get_drvdata(pdev); + + v4l2_device_unregister(&mdp->v4l2_dev); + + dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); + return 0; +} + +static int __maybe_unused mdp_suspend(struct device *dev) +{ + struct mdp_dev *mdp = dev_get_drvdata(dev); + int ret; + + atomic_set(&mdp->suspended, 1); + + if (atomic_read(&mdp->job_count)) { + ret = wait_event_timeout(mdp->callback_wq, + !atomic_read(&mdp->job_count), + 2 * HZ); + if (ret == 0) { + dev_err(dev, + "%s:flushed cmdq task incomplete, count=%d\n", + __func__, atomic_read(&mdp->job_count)); + return -EBUSY; + } + } + + return 0; +} + +static int __maybe_unused mdp_resume(struct device *dev) +{ + struct mdp_dev *mdp = dev_get_drvdata(dev); + + atomic_set(&mdp->suspended, 0); + + return 0; +} + +static const struct dev_pm_ops mdp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume) +}; + +static struct platform_driver mdp_driver = { + .probe = mdp_probe, + .remove = mdp_remove, + .driver = { + .name = MDP_MODULE_NAME, + .pm = &mdp_pm_ops, + .of_match_table = of_match_ptr(mdp_of_ids), + }, +}; + +module_platform_driver(mdp_driver); + +MODULE_AUTHOR("Ping-Hsun Wu "); +MODULE_DESCRIPTION("MediaTek image processor 3 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h new file mode 100644 index 000000000000..2ef5fbc4f25a --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_CORE_H__ +#define __MTK_MDP3_CORE_H__ + +#include +#include +#include +#include +#include "mtk-mdp3-comp.h" +#include "mtk-mdp3-vpu.h" + +#define MDP_MODULE_NAME "mtk-mdp3" +#define MDP_DEVICE_NAME "MediaTek MDP3" +#define MDP_PHANDLE_NAME "mediatek,mdp3" + +enum mdp_infra_id { + MDP_INFRA_MMSYS, + MDP_INFRA_MUTEX, + MDP_INFRA_SCP, + MDP_INFRA_MAX +}; + +enum mdp_buffer_usage { + MDP_BUFFER_USAGE_HW_READ, + MDP_BUFFER_USAGE_MDP, + MDP_BUFFER_USAGE_MDP2, + MDP_BUFFER_USAGE_ISP, + MDP_BUFFER_USAGE_WPE, +}; + +struct mdp_platform_config { + bool rdma_support_10bit; + bool rdma_rsz1_sram_sharing; + bool rdma_upsample_repeat_only; + bool rsz_disable_dcm_small_sample; + bool wrot_filter_constraint; +}; + +/* indicate which mutex is used by each pipepline */ +enum mdp_pipe_id { + MDP_PIPE_RDMA0, + MDP_PIPE_IMGI, + MDP_PIPE_WPEI, + MDP_PIPE_WPEI2, + MDP_PIPE_MAX +}; + +struct mtk_mdp_driver_data { + const struct of_device_id *mdp_probe_infra; + const struct mdp_platform_config *mdp_cfg; + const u32 *mdp_mutex_table_idx; +}; + +struct mdp_dev { + struct platform_device *pdev; + struct device *mdp_mmsys; + struct mtk_mutex *mdp_mutex[MDP_PIPE_MAX]; + struct mdp_comp *comp[MDP_MAX_COMP_COUNT]; + const struct mtk_mdp_driver_data *mdp_data; + + struct workqueue_struct *job_wq; + struct workqueue_struct *clock_wq; + struct mdp_vpu_dev vpu; + struct mtk_scp *scp; + struct rproc *rproc_handle; + /* synchronization protect for accessing vpu working buffer info */ + struct mutex vpu_lock; + s32 vpu_count; + u32 id_count; + struct ida mdp_ida; + struct cmdq_client *cmdq_clt; + wait_queue_head_t callback_wq; + + struct v4l2_device v4l2_dev; + struct video_device *m2m_vdev; + struct v4l2_m2m_dev *m2m_dev; + /* synchronization protect for m2m device operation */ + struct mutex m2m_lock; + atomic_t suspended; + atomic_t job_count; +}; + +int mdp_vpu_get_locked(struct mdp_dev *mdp); +void mdp_vpu_put_locked(struct mdp_dev *mdp); +int mdp_vpu_register(struct mdp_dev *mdp); +void mdp_vpu_unregister(struct mdp_dev *mdp); +void mdp_video_device_release(struct video_device *vdev); + +#endif /* __MTK_MDP3_CORE_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c new file mode 100644 index 000000000000..5f74ea3b7a52 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include +#include +#include "mtk-mdp3-m2m.h" + +static inline struct mdp_m2m_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mdp_m2m_ctx, fh); +} + +static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler); +} + +static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->curr_param.output; + else + return &ctx->curr_param.captures[0]; +} + +static inline void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state) +{ + atomic_or(state, &ctx->curr_param.state); +} + +static inline bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask) +{ + return ((atomic_read(&ctx->curr_param.state) & mask) == mask); +} + +static void mdp_m2m_process_done(void *priv, int vb_state) +{ + struct mdp_m2m_ctx *ctx = priv; + struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf; + + src_vbuf = (struct vb2_v4l2_buffer *) + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vbuf = (struct vb2_v4l2_buffer *) + v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC]; + src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++; + dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++; + v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true); + + v4l2_m2m_buf_done(src_vbuf, vb_state); + v4l2_m2m_buf_done(dst_vbuf, vb_state); + v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx); +} + +static void mdp_m2m_device_run(void *priv) +{ + struct mdp_m2m_ctx *ctx = priv; + struct mdp_frame *frame; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + struct img_ipi_frameparam param = {}; + struct mdp_cmdq_param task = {}; + enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR; + int ret; + + if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) { + dev_err(&ctx->mdp_dev->pdev->dev, + "mdp_m2m_ctx is in error state\n"); + goto worker_end; + } + + param.frame_no = ctx->curr_param.frame_no; + param.type = ctx->curr_param.type; + param.num_inputs = 1; + param.num_outputs = 1; + + frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + mdp_set_src_config(¶m.inputs[0], frame, &src_vb->vb2_buf); + + frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + mdp_set_dst_config(¶m.outputs[0], frame, &dst_vb->vb2_buf); + + ret = mdp_vpu_process(&ctx->vpu, ¶m); + if (ret) { + dev_err(&ctx->mdp_dev->pdev->dev, + "VPU MDP process failed: %d\n", ret); + goto worker_end; + } + + task.config = ctx->vpu.config; + task.param = ¶m; + task.composes[0] = &frame->compose; + task.cmdq_cb = NULL; + task.cb_data = NULL; + task.mdp_ctx = ctx; + + ret = mdp_cmdq_send(ctx->mdp_dev, &task); + if (ret) { + dev_err(&ctx->mdp_dev->pdev->dev, + "CMDQ sendtask failed: %d\n", ret); + goto worker_end; + } + + return; + +worker_end: + mdp_m2m_process_done(ctx, vb_state); +} + +static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct mdp_frame *capture; + struct vb2_queue *vq; + int ret; + bool out_streaming, cap_streaming; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->frame_count[MDP_M2M_SRC] = 0; + + if (V4L2_TYPE_IS_CAPTURE(q->type)) + ctx->frame_count[MDP_M2M_DST] = 0; + + capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx); + out_streaming = vb2_is_streaming(vq); + vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx); + cap_streaming = vb2_is_streaming(vq); + + /* Check to see if scaling ratio is within supported range */ + if ((V4L2_TYPE_IS_OUTPUT(q->type) && cap_streaming) || + (V4L2_TYPE_IS_CAPTURE(q->type) && out_streaming)) { + ret = mdp_check_scaling_ratio(&capture->crop.c, + &capture->compose, + capture->rotation, + ctx->curr_param.limit); + if (ret) { + dev_err(&ctx->mdp_dev->pdev->dev, + "Out of scaling range\n"); + return ret; + } + } + + if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) { + ret = mdp_vpu_get_locked(ctx->mdp_dev); + if (ret) + return ret; + + ret = mdp_vpu_ctx_init(&ctx->vpu, &ctx->mdp_dev->vpu, + MDP_DEV_M2M); + if (ret) { + dev_err(&ctx->mdp_dev->pdev->dev, + "VPU init failed %d\n", ret); + return -EINVAL; + } + mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT); + } + + return 0; +} + +static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx, + unsigned int type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return (struct vb2_v4l2_buffer *) + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + else + return (struct vb2_v4l2_buffer *) + v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); +} + +static void mdp_m2m_stop_streaming(struct vb2_queue *q) +{ + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vb; + + vb = mdp_m2m_buf_remove(ctx, q->type); + while (vb) { + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); + vb = mdp_m2m_buf_remove(ctx, q->type); + } +} + +static int mdp_m2m_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct v4l2_pix_format_mplane *pix_mp; + u32 i; + + pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp; + + /* from VIDIOC_CREATE_BUFS */ + if (*num_planes) { + if (*num_planes != pix_mp->num_planes) + return -EINVAL; + for (i = 0; i < pix_mp->num_planes; ++i) + if (sizes[i] < pix_mp->plane_fmt[i].sizeimage) + return -EINVAL; + } else {/* from VIDIOC_REQBUFS */ + *num_planes = pix_mp->num_planes; + for (i = 0; i < pix_mp->num_planes; ++i) + sizes[i] = pix_mp->plane_fmt[i].sizeimage; + } + + return 0; +} + +static int mdp_m2m_buf_prepare(struct vb2_buffer *vb) +{ + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_pix_format_mplane *pix_mp; + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + u32 i; + + v4l2_buf->field = V4L2_FIELD_NONE; + + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp; + for (i = 0; i < pix_mp->num_planes; ++i) { + vb2_set_plane_payload(vb, i, + pix_mp->plane_fmt[i].sizeimage); + } + } + return 0; +} + +static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + + v4l2_buf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void mdp_m2m_buf_queue(struct vb2_buffer *vb) +{ + struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + + v4l2_buf->field = V4L2_FIELD_NONE; + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); +} + +static const struct vb2_ops mdp_m2m_qops = { + .queue_setup = mdp_m2m_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = mdp_m2m_buf_prepare, + .start_streaming = mdp_m2m_start_streaming, + .stop_streaming = mdp_m2m_stop_streaming, + .buf_queue = mdp_m2m_buf_queue, + .buf_out_validate = mdp_m2m_buf_out_validate, +}; + +static int mdp_m2m_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card)); + + return 0; +} + +static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + return mdp_enum_fmt_mplane(f); +} + +static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + struct mdp_frame *frame; + struct v4l2_pix_format_mplane *pix_mp; + + frame = ctx_get_frame(ctx, f->type); + *f = frame->format; + pix_mp = &f->fmt.pix_mp; + pix_mp->colorspace = ctx->curr_param.colorspace; + pix_mp->xfer_func = ctx->curr_param.xfer_func; + pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc; + pix_mp->quantization = ctx->curr_param.quant; + + return 0; +} + +static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + struct mdp_frame *frame = ctx_get_frame(ctx, f->type); + struct mdp_frame *capture; + const struct mdp_format *fmt; + struct vb2_queue *vq; + + fmt = mdp_try_fmt_mplane(f, &ctx->curr_param, ctx->id); + if (!fmt) + return -EINVAL; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + frame->format = *f; + frame->mdp_fmt = fmt; + frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color); + frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ? + MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP; + + capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + capture->crop.c.left = 0; + capture->crop.c.top = 0; + capture->crop.c.width = f->fmt.pix_mp.width; + capture->crop.c.height = f->fmt.pix_mp.height; + ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace; + ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->curr_param.quant = f->fmt.pix_mp.quantization; + ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func; + } else { + capture->compose.left = 0; + capture->compose.top = 0; + capture->compose.width = f->fmt.pix_mp.width; + capture->compose.height = f->fmt.pix_mp.height; + } + + return 0; +} + +static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + + if (!mdp_try_fmt_mplane(f, &ctx->curr_param, ctx->id)) + return -EINVAL; + + return 0; +} + +static int mdp_m2m_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + struct mdp_frame *frame; + bool valid = false; + + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + valid = mdp_target_is_crop(s->target); + else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + valid = mdp_target_is_compose(s->target); + + if (!valid) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + s->r = frame->crop.c; + return 0; + case V4L2_SEL_TGT_COMPOSE: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + s->r = frame->compose; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + frame = ctx_get_frame(ctx, s->type); + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->format.fmt.pix_mp.width; + s->r.height = frame->format.fmt.pix_mp.height; + return 0; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + frame = ctx_get_frame(ctx, s->type); + s->r.left = 0; + s->r.top = 0; + s->r.width = frame->format.fmt.pix_mp.width; + s->r.height = frame->format.fmt.pix_mp.height; + return 0; + } + return -EINVAL; +} + +static int mdp_m2m_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(fh); + struct mdp_frame *frame = ctx_get_frame(ctx, s->type); + struct mdp_frame *capture; + struct v4l2_rect r; + struct device *dev = &ctx->mdp_dev->pdev->dev; + bool valid = false; + int ret; + + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + valid = (s->target == V4L2_SEL_TGT_CROP); + else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + valid = (s->target == V4L2_SEL_TGT_COMPOSE); + + if (!valid) { + dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__, + ctx->id, s->type, s->target); + return -EINVAL; + } + + ret = mdp_try_crop(ctx, &r, s, frame); + if (ret) + return ret; + capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + if (mdp_target_is_crop(s->target)) + capture->crop.c = r; + else + capture->compose = r; + + s->r = r; + + return 0; +} + +static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = { + .vidioc_querycap = mdp_m2m_querycap, + .vidioc_enum_fmt_vid_cap = mdp_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out = mdp_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = mdp_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = mdp_m2m_g_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = mdp_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = mdp_m2m_s_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = mdp_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = mdp_m2m_try_fmt_mplane, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_g_selection = mdp_m2m_g_selection, + .vidioc_s_selection = mdp_m2m_s_selection, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int mdp_m2m_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mdp_m2m_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->ops = &mdp_m2m_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = &ctx->mdp_dev->pdev->dev; + src_vq->lock = &ctx->ctx_lock; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->ops = &mdp_m2m_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = &ctx->mdp_dev->pdev->dev; + dst_vq->lock = &ctx->ctx_lock; + + return vb2_queue_init(dst_vq); +} + +static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl); + struct mdp_frame *capture; + + capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + switch (ctrl->id) { + case V4L2_CID_HFLIP: + capture->hflip = ctrl->val; + break; + case V4L2_CID_VFLIP: + capture->vflip = ctrl->val; + break; + case V4L2_CID_ROTATE: + capture->rotation = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = { + .s_ctrl = mdp_m2m_s_ctrl, +}; + +static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx) +{ + v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS); + ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, + &mdp_m2m_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, + &mdp_m2m_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, + &mdp_m2m_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + dev_err(&ctx->mdp_dev->pdev->dev, + "Failed to register controls\n"); + return err; + } + return 0; +} + +static int mdp_m2m_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct mdp_dev *mdp = video_get_drvdata(vdev); + struct mdp_m2m_ctx *ctx; + struct device *dev = &mdp->pdev->dev; + int ret; + struct v4l2_format default_format = {}; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (mutex_lock_interruptible(&mdp->m2m_lock)) { + ret = -ERESTARTSYS; + goto err_free_ctx; + } + + ctx->id = ida_alloc(&mdp->mdp_ida, GFP_KERNEL); + ctx->mdp_dev = mdp; + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data = &ctx->fh; + ret = mdp_m2m_ctrls_create(ctx); + if (ret) + goto err_exit_fh; + + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + v4l2_fh_add(&ctx->fh); + + mutex_init(&ctx->ctx_lock); + ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + dev_err(dev, "Failed to initialize m2m context\n"); + ret = PTR_ERR(ctx->m2m_ctx); + goto err_release_handler; + } + ctx->fh.m2m_ctx = ctx->m2m_ctx; + + ctx->curr_param.ctx = ctx; + ret = mdp_frameparam_init(&ctx->curr_param); + if (ret) { + dev_err(dev, "Failed to initialize mdp parameter\n"); + goto err_release_m2m_ctx; + } + + mutex_unlock(&mdp->m2m_lock); + + /* Default format */ + default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + default_format.fmt.pix_mp.width = 32; + default_format.fmt.pix_mp.height = 32; + default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; + mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + + dev_dbg(dev, "%s:[%d]", __func__, ctx->id); + + return 0; + +err_release_m2m_ctx: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_release_handler: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); +err_exit_fh: + v4l2_fh_exit(&ctx->fh); + mutex_unlock(&mdp->m2m_lock); +err_free_ctx: + kfree(ctx); + + return ret; +} + +static int mdp_m2m_release(struct file *file) +{ + struct mdp_m2m_ctx *ctx = fh_to_ctx(file->private_data); + struct mdp_dev *mdp = video_drvdata(file); + struct device *dev = &mdp->pdev->dev; + + mutex_lock(&mdp->m2m_lock); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) { + mdp_vpu_ctx_deinit(&ctx->vpu); + mdp_vpu_put_locked(mdp); + } + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + ida_free(&mdp->mdp_ida, ctx->id); + mutex_unlock(&mdp->m2m_lock); + + dev_dbg(dev, "%s:[%d]", __func__, ctx->id); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations mdp_m2m_fops = { + .owner = THIS_MODULE, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, + .open = mdp_m2m_open, + .release = mdp_m2m_release, +}; + +static const struct v4l2_m2m_ops mdp_m2m_ops = { + .device_run = mdp_m2m_device_run, +}; + +int mdp_m2m_device_register(struct mdp_dev *mdp) +{ + struct device *dev = &mdp->pdev->dev; + int ret = 0; + + mdp->m2m_vdev = video_device_alloc(); + if (!mdp->m2m_vdev) { + dev_err(dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto err_video_alloc; + } + mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + mdp->m2m_vdev->fops = &mdp_m2m_fops; + mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops; + mdp->m2m_vdev->release = mdp_video_device_release; + mdp->m2m_vdev->lock = &mdp->m2m_lock; + mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M; + mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev; + snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m", + MDP_MODULE_NAME); + video_set_drvdata(mdp->m2m_vdev, mdp); + + mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops); + if (IS_ERR(mdp->m2m_dev)) { + dev_err(dev, "Failed to initialize v4l2-m2m device\n"); + ret = PTR_ERR(mdp->m2m_dev); + goto err_m2m_init; + } + + ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(dev, "Failed to register video device\n"); + goto err_video_register; + } + + v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d", + mdp->m2m_vdev->num); + return 0; + +err_video_register: + v4l2_m2m_release(mdp->m2m_dev); +err_m2m_init: + video_device_release(mdp->m2m_vdev); +err_video_alloc: + + return ret; +} + +void mdp_m2m_device_unregister(struct mdp_dev *mdp) +{ + video_unregister_device(mdp->m2m_vdev); +} + +void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx) +{ + enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE; + + mdp_m2m_process_done(ctx, vb_state); +} diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h new file mode 100644 index 000000000000..61ddbaf1bf13 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_M2M_H__ +#define __MTK_MDP3_M2M_H__ + +#include +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-vpu.h" +#include "mtk-mdp3-regs.h" + +#define MDP_MAX_CTRLS 10 + +enum { + MDP_M2M_SRC = 0, + MDP_M2M_DST = 1, + MDP_M2M_MAX, +}; + +struct mdp_m2m_ctrls { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *rotate; +}; + +struct mdp_m2m_ctx { + u32 id; + struct mdp_dev *mdp_dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct mdp_m2m_ctrls ctrls; + struct v4l2_m2m_ctx *m2m_ctx; + struct mdp_vpu_ctx vpu; + u32 frame_count[MDP_M2M_MAX]; + + struct mdp_frameparam curr_param; + /* synchronization protect for mdp m2m context */ + struct mutex ctx_lock; +}; + +int mdp_m2m_device_register(struct mdp_dev *mdp); +void mdp_m2m_device_unregister(struct mdp_dev *mdp); +void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx); + +#endif /* __MTK_MDP3_M2M_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c new file mode 100644 index 000000000000..4e84a37ecdfc --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include +#include "mtk-mdp3-core.h" +#include "mtk-mdp3-regs.h" +#include "mtk-mdp3-m2m.h" + +/* + * All 10-bit related formats are not added in the basic format list, + * please add the corresponding format settings before use. + */ +static const struct mdp_format mdp_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_GREY, + .mdp_color = MDP_COLOR_GREY, + .depth = { 8 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .mdp_color = MDP_COLOR_BGR565, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565, + .mdp_color = MDP_COLOR_RGB565, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_RGB24, + .mdp_color = MDP_COLOR_RGB888, + .depth = { 24 }, + .row_depth = { 24 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_BGR24, + .mdp_color = MDP_COLOR_BGR888, + .depth = { 24 }, + .row_depth = { 24 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_ABGR32, + .mdp_color = MDP_COLOR_BGRA8888, + .depth = { 32 }, + .row_depth = { 32 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_ARGB32, + .mdp_color = MDP_COLOR_ARGB8888, + .depth = { 32 }, + .row_depth = { 32 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_UYVY, + .mdp_color = MDP_COLOR_UYVY, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_VYUY, + .mdp_color = MDP_COLOR_VYUY, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YUYV, + .mdp_color = MDP_COLOR_YUYV, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVYU, + .mdp_color = MDP_COLOR_YVYU, + .depth = { 16 }, + .row_depth = { 16 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420, + .mdp_color = MDP_COLOR_I420, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420, + .mdp_color = MDP_COLOR_YV12, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV12, + .mdp_color = MDP_COLOR_NV12, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV21, + .mdp_color = MDP_COLOR_NV21, + .depth = { 12 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV16, + .mdp_color = MDP_COLOR_NV16, + .depth = { 16 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV61, + .mdp_color = MDP_COLOR_NV61, + .depth = { 16 }, + .row_depth = { 8 }, + .num_planes = 1, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV24, + .mdp_color = MDP_COLOR_NV24, + .depth = { 24 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV42, + .mdp_color = MDP_COLOR_NV42, + .depth = { 24 }, + .row_depth = { 8 }, + .num_planes = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_MT21C, + .mdp_color = MDP_COLOR_420_BLK_UFO, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 4, + .halign = 5, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_MM21, + .mdp_color = MDP_COLOR_420_BLK, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 4, + .halign = 5, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV12M, + .mdp_color = MDP_COLOR_NV12, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV21M, + .mdp_color = MDP_COLOR_NV21, + .depth = { 8, 4 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_NV16M, + .mdp_color = MDP_COLOR_NV16, + .depth = { 8, 8 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_NV61M, + .mdp_color = MDP_COLOR_NV61, + .depth = { 8, 8 }, + .row_depth = { 8, 8 }, + .num_planes = 2, + .walign = 1, + .flags = MDP_FMT_FLAG_OUTPUT, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420M, + .mdp_color = MDP_COLOR_I420, + .depth = { 8, 2, 2 }, + .row_depth = { 8, 4, 4 }, + .num_planes = 3, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420M, + .mdp_color = MDP_COLOR_YV12, + .depth = { 8, 2, 2 }, + .row_depth = { 8, 4, 4 }, + .num_planes = 3, + .walign = 1, + .halign = 1, + .flags = MDP_FMT_FLAG_OUTPUT | MDP_FMT_FLAG_CAPTURE, + } +}; + +static const struct mdp_limit mdp_def_limit = { + .out_limit = { + .wmin = 16, + .hmin = 16, + .wmax = 8176, + .hmax = 8176, + }, + .cap_limit = { + .wmin = 2, + .hmin = 2, + .wmax = 8176, + .hmax = 8176, + }, + .h_scale_up_max = 32, + .v_scale_up_max = 32, + .h_scale_down_max = 20, + .v_scale_down_max = 128, +}; + +static const struct mdp_format *mdp_find_fmt(u32 pixelformat, u32 type) +{ + u32 i, flag; + + flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : + MDP_FMT_FLAG_CAPTURE; + for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { + if (!(mdp_formats[i].flags & flag)) + continue; + if (mdp_formats[i].pixelformat == pixelformat) + return &mdp_formats[i]; + } + return NULL; +} + +static const struct mdp_format *mdp_find_fmt_by_index(u32 index, u32 type) +{ + u32 i, flag, num = 0; + + flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT : + MDP_FMT_FLAG_CAPTURE; + for (i = 0; i < ARRAY_SIZE(mdp_formats); ++i) { + if (!(mdp_formats[i].flags & flag)) + continue; + if (index == num) + return &mdp_formats[i]; + num++; + } + return NULL; +} + +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f, + u32 mdp_color) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + + if (MDP_COLOR_IS_RGB(mdp_color)) + return MDP_YCBCR_PROFILE_FULL_BT601; + + switch (pix_mp->colorspace) { + case V4L2_COLORSPACE_JPEG: + return MDP_YCBCR_PROFILE_JPEG; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_DCI_P3: + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) + return MDP_YCBCR_PROFILE_FULL_BT709; + return MDP_YCBCR_PROFILE_BT709; + case V4L2_COLORSPACE_BT2020: + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) + return MDP_YCBCR_PROFILE_FULL_BT2020; + return MDP_YCBCR_PROFILE_BT2020; + default: + if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE) + return MDP_YCBCR_PROFILE_FULL_BT601; + return MDP_YCBCR_PROFILE_BT601; + } +} + +static void mdp_bound_align_image(u32 *w, u32 *h, + struct v4l2_frmsize_stepwise *s, + unsigned int salign) +{ + unsigned int org_w, org_h; + + org_w = *w; + org_h = *h; + v4l_bound_align_image(w, s->min_width, s->max_width, s->step_width, + h, s->min_height, s->max_height, s->step_height, + salign); + + s->min_width = org_w; + s->min_height = org_h; + v4l2_apply_frmsize_constraints(w, h, s); +} + +static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align) +{ + unsigned int mask; + + if (min < 0 || max < 0) + return -ERANGE; + + /* Bits that must be zero to be aligned */ + mask = ~((1 << align) - 1); + + min = 0 ? 0 : ((min + ~mask) & mask); + max = max & mask; + if ((unsigned int)min > (unsigned int)max) + return -ERANGE; + + /* Clamp to aligned min and max */ + *x = clamp(*x, min, max); + + /* Round to nearest aligned value */ + if (align) + *x = (*x + (1 << (align - 1))) & mask; + return 0; +} + +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f) +{ + const struct mdp_format *fmt; + + fmt = mdp_find_fmt_by_index(f->index, f->type); + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixelformat; + return 0; +} + +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, + struct mdp_frameparam *param, + u32 ctx_id) +{ + struct device *dev = ¶m->ctx->mdp_dev->pdev->dev; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct mdp_format *fmt; + const struct mdp_pix_limit *pix_limit; + struct v4l2_frmsize_stepwise s; + u32 org_w, org_h; + unsigned int i; + + fmt = mdp_find_fmt(pix_mp->pixelformat, f->type); + if (!fmt) { + fmt = mdp_find_fmt_by_index(0, f->type); + if (!fmt) { + dev_dbg(dev, "%d: pixelformat %c%c%c%c invalid", ctx_id, + (pix_mp->pixelformat & 0xff), + (pix_mp->pixelformat >> 8) & 0xff, + (pix_mp->pixelformat >> 16) & 0xff, + (pix_mp->pixelformat >> 24) & 0xff); + return NULL; + } + } + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->flags = 0; + pix_mp->pixelformat = fmt->pixelformat; + if (V4L2_TYPE_IS_CAPTURE(f->type)) { + pix_mp->colorspace = param->colorspace; + pix_mp->xfer_func = param->xfer_func; + pix_mp->ycbcr_enc = param->ycbcr_enc; + pix_mp->quantization = param->quant; + } + + pix_limit = V4L2_TYPE_IS_OUTPUT(f->type) ? ¶m->limit->out_limit : + ¶m->limit->cap_limit; + s.min_width = pix_limit->wmin; + s.max_width = pix_limit->wmax; + s.step_width = fmt->walign; + s.min_height = pix_limit->hmin; + s.max_height = pix_limit->hmax; + s.step_height = fmt->halign; + org_w = pix_mp->width; + org_h = pix_mp->height; + + mdp_bound_align_image(&pix_mp->width, &pix_mp->height, &s, fmt->salign); + if (org_w != pix_mp->width || org_h != pix_mp->height) + dev_dbg(dev, "%d: size change: %ux%u to %ux%u", ctx_id, + org_w, org_h, pix_mp->width, pix_mp->height); + + if (pix_mp->num_planes && pix_mp->num_planes != fmt->num_planes) + dev_dbg(dev, "%d num of planes change: %u to %u", ctx_id, + pix_mp->num_planes, fmt->num_planes); + pix_mp->num_planes = fmt->num_planes; + + for (i = 0; i < pix_mp->num_planes; ++i) { + u32 min_bpl = (pix_mp->width * fmt->row_depth[i]) >> 3; + u32 max_bpl = (pix_limit->wmax * fmt->row_depth[i]) >> 3; + u32 bpl = pix_mp->plane_fmt[i].bytesperline; + u32 min_si, max_si; + u32 si = pix_mp->plane_fmt[i].sizeimage; + + bpl = clamp(bpl, min_bpl, max_bpl); + pix_mp->plane_fmt[i].bytesperline = bpl; + + min_si = (bpl * pix_mp->height * fmt->depth[i]) / + fmt->row_depth[i]; + max_si = (bpl * s.max_height * fmt->depth[i]) / + fmt->row_depth[i]; + + si = clamp(si, min_si, max_si); + pix_mp->plane_fmt[i].sizeimage = si; + + dev_dbg(dev, "%d: p%u, bpl:%u [%u, %u], sizeimage:%u [%u, %u]", + ctx_id, i, bpl, min_bpl, max_bpl, si, min_si, max_si); + } + + return fmt; +} + +static int mdp_clamp_start(s32 *x, int min, int max, unsigned int align, + u32 flags) +{ + if (flags & V4L2_SEL_FLAG_GE) + max = *x; + if (flags & V4L2_SEL_FLAG_LE) + min = *x; + return mdp_clamp_align(x, min, max, align); +} + +static int mdp_clamp_end(s32 *x, int min, int max, unsigned int align, + u32 flags) +{ + if (flags & V4L2_SEL_FLAG_GE) + min = *x; + if (flags & V4L2_SEL_FLAG_LE) + max = *x; + return mdp_clamp_align(x, min, max, align); +} + +int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r, + const struct v4l2_selection *s, struct mdp_frame *frame) +{ + struct device *dev = &ctx->mdp_dev->pdev->dev; + s32 left, top, right, bottom; + u32 framew, frameh, walign, halign; + int ret; + + dev_dbg(dev, "%d target:%d, set:(%d,%d) %ux%u", ctx->id, + s->target, s->r.left, s->r.top, s->r.width, s->r.height); + + left = s->r.left; + top = s->r.top; + right = s->r.left + s->r.width; + bottom = s->r.top + s->r.height; + framew = frame->format.fmt.pix_mp.width; + frameh = frame->format.fmt.pix_mp.height; + + if (mdp_target_is_crop(s->target)) { + walign = 1; + halign = 1; + } else { + walign = frame->mdp_fmt->walign; + halign = frame->mdp_fmt->halign; + } + + dev_dbg(dev, "%d align:%u,%u, bound:%ux%u", ctx->id, + walign, halign, framew, frameh); + + ret = mdp_clamp_start(&left, 0, right, walign, s->flags); + if (ret) + return ret; + ret = mdp_clamp_start(&top, 0, bottom, halign, s->flags); + if (ret) + return ret; + ret = mdp_clamp_end(&right, left, framew, walign, s->flags); + if (ret) + return ret; + ret = mdp_clamp_end(&bottom, top, frameh, halign, s->flags); + if (ret) + return ret; + + r->left = left; + r->top = top; + r->width = right - left; + r->height = bottom - top; + + dev_dbg(dev, "%d crop:(%d,%d) %ux%u", ctx->id, + r->left, r->top, r->width, r->height); + return 0; +} + +int mdp_check_scaling_ratio(const struct v4l2_rect *crop, + const struct v4l2_rect *compose, s32 rotation, + const struct mdp_limit *limit) +{ + u32 crop_w, crop_h, comp_w, comp_h; + + crop_w = crop->width; + crop_h = crop->height; + if (90 == rotation || 270 == rotation) { + comp_w = compose->height; + comp_h = compose->width; + } else { + comp_w = compose->width; + comp_h = compose->height; + } + + if ((crop_w / comp_w) > limit->h_scale_down_max || + (crop_h / comp_h) > limit->v_scale_down_max || + (comp_w / crop_w) > limit->h_scale_up_max || + (comp_h / crop_h) > limit->v_scale_up_max) + return -ERANGE; + return 0; +} + +/* Stride that is accepted by MDP HW */ +static u32 mdp_fmt_get_stride(const struct mdp_format *fmt, + u32 bytesperline, unsigned int plane) +{ + enum mdp_color c = fmt->mdp_color; + u32 stride; + + stride = (bytesperline * MDP_COLOR_BITS_PER_PIXEL(c)) + / fmt->row_depth[0]; + if (plane == 0) + return stride; + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) { + if (MDP_COLOR_IS_BLOCK_MODE(c)) + stride = stride / 2; + return stride; + } + return 0; +} + +/* Stride that is accepted by MDP HW of format with contiguous planes */ +static u32 mdp_fmt_get_stride_contig(const struct mdp_format *fmt, + u32 pix_stride, unsigned int plane) +{ + enum mdp_color c = fmt->mdp_color; + u32 stride = pix_stride; + + if (plane == 0) + return stride; + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) { + stride = stride >> MDP_COLOR_GET_H_SUBSAMPLE(c); + if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c)) + stride = stride * 2; + return stride; + } + return 0; +} + +/* Plane size that is accepted by MDP HW */ +static u32 mdp_fmt_get_plane_size(const struct mdp_format *fmt, + u32 stride, u32 height, unsigned int plane) +{ + enum mdp_color c = fmt->mdp_color; + u32 bytesperline; + + bytesperline = (stride * fmt->row_depth[0]) + / MDP_COLOR_BITS_PER_PIXEL(c); + if (plane == 0) + return bytesperline * height; + if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) { + height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c); + if (MDP_COLOR_IS_BLOCK_MODE(c)) + bytesperline = bytesperline * 2; + return bytesperline * height; + } + return 0; +} + +static void mdp_prepare_buffer(struct img_image_buffer *b, + struct mdp_frame *frame, struct vb2_buffer *vb) +{ + struct v4l2_pix_format_mplane *pix_mp = &frame->format.fmt.pix_mp; + unsigned int i; + + b->format.colorformat = frame->mdp_fmt->mdp_color; + b->format.ycbcr_prof = frame->ycbcr_prof; + for (i = 0; i < pix_mp->num_planes; ++i) { + u32 stride = mdp_fmt_get_stride(frame->mdp_fmt, + pix_mp->plane_fmt[i].bytesperline, i); + + b->format.plane_fmt[i].stride = stride; + b->format.plane_fmt[i].size = + mdp_fmt_get_plane_size(frame->mdp_fmt, stride, + pix_mp->height, i); + b->iova[i] = vb2_dma_contig_plane_dma_addr(vb, i); + } + for (; i < MDP_COLOR_GET_PLANE_COUNT(b->format.colorformat); ++i) { + u32 stride = mdp_fmt_get_stride_contig(frame->mdp_fmt, + b->format.plane_fmt[0].stride, i); + + b->format.plane_fmt[i].stride = stride; + b->format.plane_fmt[i].size = + mdp_fmt_get_plane_size(frame->mdp_fmt, stride, + pix_mp->height, i); + b->iova[i] = b->iova[i - 1] + b->format.plane_fmt[i - 1].size; + } + b->usage = frame->usage; +} + +void mdp_set_src_config(struct img_input *in, + struct mdp_frame *frame, struct vb2_buffer *vb) +{ + in->buffer.format.width = frame->format.fmt.pix_mp.width; + in->buffer.format.height = frame->format.fmt.pix_mp.height; + mdp_prepare_buffer(&in->buffer, frame, vb); +} + +static u32 mdp_to_fixed(u32 *r, struct v4l2_fract *f) +{ + u32 q; + + if (f->denominator == 0) { + *r = 0; + return 0; + } + + q = f->numerator / f->denominator; + *r = div_u64(((u64)f->numerator - q * f->denominator) << + IMG_SUBPIXEL_SHIFT, f->denominator); + return q; +} + +static void mdp_set_src_crop(struct img_crop *c, struct mdp_crop *crop) +{ + c->left = crop->c.left + + mdp_to_fixed(&c->left_subpix, &crop->left_subpix); + c->top = crop->c.top + + mdp_to_fixed(&c->top_subpix, &crop->top_subpix); + c->width = crop->c.width + + mdp_to_fixed(&c->width_subpix, &crop->width_subpix); + c->height = crop->c.height + + mdp_to_fixed(&c->height_subpix, &crop->height_subpix); +} + +static void mdp_set_orientation(struct img_output *out, + s32 rotation, bool hflip, bool vflip) +{ + u8 flip = 0; + + if (hflip) + flip ^= 1; + if (vflip) { + /* + * A vertical flip is equivalent to + * a 180-degree rotation with a horizontal flip + */ + rotation += 180; + flip ^= 1; + } + + out->rotation = rotation % 360; + if (flip != 0) + out->flags |= IMG_CTRL_FLAG_HFLIP; + else + out->flags &= ~IMG_CTRL_FLAG_HFLIP; +} + +void mdp_set_dst_config(struct img_output *out, + struct mdp_frame *frame, struct vb2_buffer *vb) +{ + out->buffer.format.width = frame->compose.width; + out->buffer.format.height = frame->compose.height; + mdp_prepare_buffer(&out->buffer, frame, vb); + mdp_set_src_crop(&out->crop, &frame->crop); + mdp_set_orientation(out, frame->rotation, frame->hflip, frame->vflip); +} + +int mdp_frameparam_init(struct mdp_frameparam *param) +{ + struct mdp_frame *frame; + + if (!param) + return -EINVAL; + + INIT_LIST_HEAD(¶m->list); + param->limit = &mdp_def_limit; + param->type = MDP_STREAM_TYPE_BITBLT; + + frame = ¶m->output; + frame->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0); + frame->ycbcr_prof = + mdp_map_ycbcr_prof_mplane(&frame->format, + frame->mdp_fmt->mdp_color); + frame->usage = MDP_BUFFER_USAGE_HW_READ; + + param->num_captures = 1; + frame = ¶m->captures[0]; + frame->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + frame->mdp_fmt = mdp_try_fmt_mplane(&frame->format, param, 0); + frame->ycbcr_prof = + mdp_map_ycbcr_prof_mplane(&frame->format, + frame->mdp_fmt->mdp_color); + frame->usage = MDP_BUFFER_USAGE_MDP; + frame->crop.c.width = param->output.format.fmt.pix_mp.width; + frame->crop.c.height = param->output.format.fmt.pix_mp.height; + frame->compose.width = frame->format.fmt.pix_mp.width; + frame->compose.height = frame->format.fmt.pix_mp.height; + + return 0; +} diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h new file mode 100644 index 000000000000..f995e536d45f --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_REGS_H__ +#define __MTK_MDP3_REGS_H__ + +#include +#include +#include "mtk-img-ipi.h" + +/* + * MDP native color code + * Plane count: 1, 2, 3 + * H-subsample: 0, 1, 2 + * V-subsample: 0, 1 + * Color group: 0-RGB, 1-YUV, 2-raw + */ +#define MDP_COLOR(PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\ + (((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\ + ((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\ + ((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0)) + +#define MDP_COLOR_IS_10BIT_PACKED(c) ((0x08000000 & (c)) >> 27) +#define MDP_COLOR_IS_10BIT_LOOSE(c) (((0x0c000000 & (c)) >> 26) == 1) +#define MDP_COLOR_IS_10BIT_TILE(c) (((0x0c000000 & (c)) >> 26) == 3) +#define MDP_COLOR_IS_UFP(c) ((0x02000000 & (c)) >> 25) +#define MDP_COLOR_IS_INTERLACED(c) ((0x01000000 & (c)) >> 24) +#define MDP_COLOR_IS_BLOCK_MODE(c) ((0x00800000 & (c)) >> 23) +#define MDP_COLOR_GET_PLANE_COUNT(c) ((0x00600000 & (c)) >> 21) +#define MDP_COLOR_GET_H_SUBSAMPLE(c) ((0x00180000 & (c)) >> 19) +#define MDP_COLOR_GET_V_SUBSAMPLE(c) ((0x00040000 & (c)) >> 18) +#define MDP_COLOR_BITS_PER_PIXEL(c) ((0x0003ff00 & (c)) >> 8) +#define MDP_COLOR_GET_GROUP(c) ((0x000000c0 & (c)) >> 6) +#define MDP_COLOR_IS_SWAPPED(c) ((0x00000020 & (c)) >> 5) +#define MDP_COLOR_GET_UNIQUE_ID(c) ((0x0000001f & (c)) >> 0) +#define MDP_COLOR_GET_HW_FORMAT(c) ((0x0000001f & (c)) >> 0) + +#define MDP_COLOR_IS_RGB(c) (MDP_COLOR_GET_GROUP(c) == 0) +#define MDP_COLOR_IS_YUV(c) (MDP_COLOR_GET_GROUP(c) == 1) + +enum mdp_color { + MDP_COLOR_UNKNOWN = 0, + + //MDP_COLOR_FULLG8, + MDP_COLOR_FULLG8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 21), + MDP_COLOR_FULLG8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 21), + MDP_COLOR_FULLG8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 21), + MDP_COLOR_FULLG8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 21), + MDP_COLOR_FULLG8 = MDP_COLOR_FULLG8_BGGR, + + //MDP_COLOR_FULLG10, + MDP_COLOR_FULLG10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 21), + MDP_COLOR_FULLG10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 21), + MDP_COLOR_FULLG10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 21), + MDP_COLOR_FULLG10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 21), + MDP_COLOR_FULLG10 = MDP_COLOR_FULLG10_BGGR, + + //MDP_COLOR_FULLG12, + MDP_COLOR_FULLG12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 21), + MDP_COLOR_FULLG12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 21), + MDP_COLOR_FULLG12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 21), + MDP_COLOR_FULLG12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 21), + MDP_COLOR_FULLG12 = MDP_COLOR_FULLG12_BGGR, + + //MDP_COLOR_FULLG14, + MDP_COLOR_FULLG14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 21), + MDP_COLOR_FULLG14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 21), + MDP_COLOR_FULLG14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 21), + MDP_COLOR_FULLG14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 21), + MDP_COLOR_FULLG14 = MDP_COLOR_FULLG14_BGGR, + + MDP_COLOR_UFO10 = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 24), + + //MDP_COLOR_BAYER8, + MDP_COLOR_BAYER8_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 20), + MDP_COLOR_BAYER8_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 8, 2, 0, 20), + MDP_COLOR_BAYER8_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 8, 2, 0, 20), + MDP_COLOR_BAYER8_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 8, 2, 0, 20), + MDP_COLOR_BAYER8 = MDP_COLOR_BAYER8_BGGR, + + //MDP_COLOR_BAYER10, + MDP_COLOR_BAYER10_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 20), + MDP_COLOR_BAYER10_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 10, 2, 0, 20), + MDP_COLOR_BAYER10_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 10, 2, 0, 20), + MDP_COLOR_BAYER10_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 10, 2, 0, 20), + MDP_COLOR_BAYER10 = MDP_COLOR_BAYER10_BGGR, + + //MDP_COLOR_BAYER12, + MDP_COLOR_BAYER12_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 20), + MDP_COLOR_BAYER12_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 12, 2, 0, 20), + MDP_COLOR_BAYER12_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 12, 2, 0, 20), + MDP_COLOR_BAYER12_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 12, 2, 0, 20), + MDP_COLOR_BAYER12 = MDP_COLOR_BAYER12_BGGR, + + //MDP_COLOR_BAYER14, + MDP_COLOR_BAYER14_RGGB = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 20), + MDP_COLOR_BAYER14_GRBG = MDP_COLOR(0, 0, 0, 1, 0, 1, 14, 2, 0, 20), + MDP_COLOR_BAYER14_GBRG = MDP_COLOR(0, 0, 0, 1, 1, 0, 14, 2, 0, 20), + MDP_COLOR_BAYER14_BGGR = MDP_COLOR(0, 0, 0, 1, 1, 1, 14, 2, 0, 20), + MDP_COLOR_BAYER14 = MDP_COLOR_BAYER14_BGGR, + + MDP_COLOR_RGB48 = MDP_COLOR(0, 0, 0, 1, 0, 0, 48, 0, 0, 23), + /* For bayer+mono raw-16 */ + MDP_COLOR_RGB565_RAW = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 2, 0, 0), + + MDP_COLOR_BAYER8_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 2, 0, 22), + MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 10, 2, 0, 22), + MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 12, 2, 0, 22), + MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 1, 0, 0, 14, 2, 0, 22), + + /* Unified formats */ + MDP_COLOR_GREY = MDP_COLOR(0, 0, 0, 1, 0, 0, 8, 1, 0, 7), + + MDP_COLOR_RGB565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 0, 0), + MDP_COLOR_BGR565 = MDP_COLOR(0, 0, 0, 1, 0, 0, 16, 0, 1, 0), + MDP_COLOR_RGB888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 1, 1), + MDP_COLOR_BGR888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 0, 0, 1), + MDP_COLOR_RGBA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 2), + MDP_COLOR_BGRA8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 2), + MDP_COLOR_ARGB8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 1, 3), + MDP_COLOR_ABGR8888 = MDP_COLOR(0, 0, 0, 1, 0, 0, 32, 0, 0, 3), + + MDP_COLOR_UYVY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 4), + MDP_COLOR_VYUY = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 4), + MDP_COLOR_YUYV = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 0, 5), + MDP_COLOR_YVYU = MDP_COLOR(0, 0, 0, 1, 1, 0, 16, 1, 1, 5), + + MDP_COLOR_I420 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 0, 8), + MDP_COLOR_YV12 = MDP_COLOR(0, 0, 0, 3, 1, 1, 8, 1, 1, 8), + MDP_COLOR_I422 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 0, 9), + MDP_COLOR_YV16 = MDP_COLOR(0, 0, 0, 3, 1, 0, 8, 1, 1, 9), + MDP_COLOR_I444 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 0, 10), + MDP_COLOR_YV24 = MDP_COLOR(0, 0, 0, 3, 0, 0, 8, 1, 1, 10), + + MDP_COLOR_NV12 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 0, 12), + MDP_COLOR_NV21 = MDP_COLOR(0, 0, 0, 2, 1, 1, 8, 1, 1, 12), + MDP_COLOR_NV16 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 0, 13), + MDP_COLOR_NV61 = MDP_COLOR(0, 0, 0, 2, 1, 0, 8, 1, 1, 13), + MDP_COLOR_NV24 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 0, 14), + MDP_COLOR_NV42 = MDP_COLOR(0, 0, 0, 2, 0, 0, 8, 1, 1, 14), + + /* MediaTek proprietary formats */ + /* UFO encoded block mode */ + MDP_COLOR_420_BLK_UFO = MDP_COLOR(0, 0, 5, 2, 1, 1, 256, 1, 0, 12), + /* Block mode */ + MDP_COLOR_420_BLK = MDP_COLOR(0, 0, 1, 2, 1, 1, 256, 1, 0, 12), + /* Block mode + field mode */ + MDP_COLOR_420_BLKI = MDP_COLOR(0, 0, 3, 2, 1, 1, 256, 1, 0, 12), + /* Block mode */ + MDP_COLOR_422_BLK = MDP_COLOR(0, 0, 1, 1, 1, 0, 512, 1, 0, 4), + + MDP_COLOR_IYU2 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 25), + MDP_COLOR_YUV444 = MDP_COLOR(0, 0, 0, 1, 0, 0, 24, 1, 0, 30), + + /* Packed 10-bit formats */ + MDP_COLOR_RGBA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 1, 2), + MDP_COLOR_BGRA1010102 = MDP_COLOR(1, 0, 0, 1, 0, 0, 32, 0, 0, 2), + /* Packed 10-bit UYVY */ + MDP_COLOR_UYVY_10P = MDP_COLOR(1, 0, 0, 1, 1, 0, 20, 1, 0, 4), + /* Packed 10-bit NV21 */ + MDP_COLOR_NV21_10P = MDP_COLOR(1, 0, 0, 2, 1, 1, 10, 1, 1, 12), + /* 10-bit block mode */ + MDP_COLOR_420_BLK_10_H = MDP_COLOR(1, 0, 1, 2, 1, 1, 320, 1, 0, 12), + /* 10-bit HEVC tile mode */ + MDP_COLOR_420_BLK_10_V = MDP_COLOR(1, 1, 1, 2, 1, 1, 320, 1, 0, 12), + /* UFO encoded 10-bit block mode */ + MDP_COLOR_420_BLK_U10_H = MDP_COLOR(1, 0, 5, 2, 1, 1, 320, 1, 0, 12), + /* UFO encoded 10-bit HEVC tile mode */ + MDP_COLOR_420_BLK_U10_V = MDP_COLOR(1, 1, 5, 2, 1, 1, 320, 1, 0, 12), + + /* Loose 10-bit formats */ + MDP_COLOR_UYVY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 4), + MDP_COLOR_VYUY_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 4), + MDP_COLOR_YUYV_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 0, 5), + MDP_COLOR_YVYU_10L = MDP_COLOR(0, 1, 0, 1, 1, 0, 20, 1, 1, 5), + MDP_COLOR_NV12_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 0, 12), + MDP_COLOR_NV21_10L = MDP_COLOR(0, 1, 0, 2, 1, 1, 10, 1, 1, 12), + MDP_COLOR_NV16_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 0, 13), + MDP_COLOR_NV61_10L = MDP_COLOR(0, 1, 0, 2, 1, 0, 10, 1, 1, 13), + MDP_COLOR_YV12_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 1, 8), + MDP_COLOR_I420_10L = MDP_COLOR(0, 1, 0, 3, 1, 1, 10, 1, 0, 8), +}; + +static inline bool MDP_COLOR_IS_UV_COPLANE(enum mdp_color c) +{ + return (MDP_COLOR_GET_PLANE_COUNT(c) == 2 && MDP_COLOR_IS_YUV(c)); +} + +/* Minimum Y stride that is accepted by MDP HW */ +static inline u32 mdp_color_get_min_y_stride(enum mdp_color c, u32 width) +{ + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) + 4) >> 3; +} + +/* Minimum UV stride that is accepted by MDP HW */ +static inline u32 mdp_color_get_min_uv_stride(enum mdp_color c, u32 width) +{ + u32 min_stride; + + if (MDP_COLOR_GET_PLANE_COUNT(c) == 1) + return 0; + min_stride = mdp_color_get_min_y_stride(c, width) + >> MDP_COLOR_GET_H_SUBSAMPLE(c); + if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c)) + min_stride = min_stride * 2; + return min_stride; +} + +/* Minimum Y plane size that is necessary in buffer */ +static inline u32 mdp_color_get_min_y_size(enum mdp_color c, + u32 width, u32 height) +{ + if (MDP_COLOR_IS_BLOCK_MODE(c)) + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height; + return mdp_color_get_min_y_stride(c, width) * height; +} + +/* Minimum UV plane size that is necessary in buffer */ +static inline u32 mdp_color_get_min_uv_size(enum mdp_color c, + u32 width, u32 height) +{ + height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c); + if (MDP_COLOR_IS_BLOCK_MODE(c) && (MDP_COLOR_GET_PLANE_COUNT(c) > 1)) + return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height; + return mdp_color_get_min_uv_stride(c, width) * height; +} + +/* Combine colorspace, xfer_func, ycbcr_encoding, and quantization */ +enum mdp_ycbcr_profile { + /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_LIM_RANGE */ + MDP_YCBCR_PROFILE_BT601, + /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_LIM_RANGE */ + MDP_YCBCR_PROFILE_BT709, + /* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_FULL_RANGE */ + MDP_YCBCR_PROFILE_JPEG, + MDP_YCBCR_PROFILE_FULL_BT601 = MDP_YCBCR_PROFILE_JPEG, + + /* Colorspaces not support for capture */ + /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_LIM_RANGE */ + MDP_YCBCR_PROFILE_BT2020, + /* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_FULL_RANGE */ + MDP_YCBCR_PROFILE_FULL_BT709, + /* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_FULL_RANGE */ + MDP_YCBCR_PROFILE_FULL_BT2020, +}; + +#define MDP_FMT_FLAG_OUTPUT BIT(0) +#define MDP_FMT_FLAG_CAPTURE BIT(1) + +struct mdp_format { + u32 pixelformat; + u32 mdp_color; + u8 depth[VIDEO_MAX_PLANES]; + u8 row_depth[VIDEO_MAX_PLANES]; + u8 num_planes; + u8 walign; + u8 halign; + u8 salign; + u32 flags; +}; + +struct mdp_pix_limit { + u32 wmin; + u32 hmin; + u32 wmax; + u32 hmax; +}; + +struct mdp_limit { + struct mdp_pix_limit out_limit; + struct mdp_pix_limit cap_limit; + u32 h_scale_up_max; + u32 v_scale_up_max; + u32 h_scale_down_max; + u32 v_scale_down_max; +}; + +enum mdp_stream_type { + MDP_STREAM_TYPE_UNKNOWN, + MDP_STREAM_TYPE_BITBLT, + MDP_STREAM_TYPE_GPU_BITBLT, + MDP_STREAM_TYPE_DUAL_BITBLT, + MDP_STREAM_TYPE_2ND_BITBLT, + MDP_STREAM_TYPE_ISP_IC, + MDP_STREAM_TYPE_ISP_VR, + MDP_STREAM_TYPE_ISP_ZSD, + MDP_STREAM_TYPE_ISP_IP, + MDP_STREAM_TYPE_ISP_VSS, + MDP_STREAM_TYPE_ISP_ZSD_SLOW, + MDP_STREAM_TYPE_WPE, + MDP_STREAM_TYPE_WPE2, +}; + +struct mdp_crop { + struct v4l2_rect c; + struct v4l2_fract left_subpix; + struct v4l2_fract top_subpix; + struct v4l2_fract width_subpix; + struct v4l2_fract height_subpix; +}; + +struct mdp_frame { + struct v4l2_format format; + const struct mdp_format *mdp_fmt; + u32 ycbcr_prof; /* enum mdp_ycbcr_profile */ + u32 usage; /* enum mdp_buffer_usage */ + struct mdp_crop crop; + struct v4l2_rect compose; + s32 rotation; + u32 hflip:1; + u32 vflip:1; + u32 hdr:1; + u32 dre:1; + u32 sharpness:1; + u32 dither:1; +}; + +static inline bool mdp_target_is_crop(u32 target) +{ + return (target == V4L2_SEL_TGT_CROP) || + (target == V4L2_SEL_TGT_CROP_DEFAULT) || + (target == V4L2_SEL_TGT_CROP_BOUNDS); +} + +static inline bool mdp_target_is_compose(u32 target) +{ + return (target == V4L2_SEL_TGT_COMPOSE) || + (target == V4L2_SEL_TGT_COMPOSE_DEFAULT) || + (target == V4L2_SEL_TGT_COMPOSE_BOUNDS); +} + +#define MDP_MAX_CAPTURES IMG_MAX_HW_OUTPUTS + +#define MDP_VPU_INIT BIT(0) +#define MDP_M2M_CTX_ERROR BIT(1) + +struct mdp_frameparam { + struct list_head list; + struct mdp_m2m_ctx *ctx; + atomic_t state; + const struct mdp_limit *limit; + u32 type; /* enum mdp_stream_type */ + u32 frame_no; + struct mdp_frame output; + struct mdp_frame captures[MDP_MAX_CAPTURES]; + u32 num_captures; + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_xfer_func xfer_func; + enum v4l2_quantization quant; +}; + +int mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f); +const struct mdp_format *mdp_try_fmt_mplane(struct v4l2_format *f, + struct mdp_frameparam *param, + u32 ctx_id); +enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f, + u32 mdp_color); +int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r, + const struct v4l2_selection *s, struct mdp_frame *frame); +int mdp_check_scaling_ratio(const struct v4l2_rect *crop, + const struct v4l2_rect *compose, s32 rotation, + const struct mdp_limit *limit); +void mdp_set_src_config(struct img_input *in, + struct mdp_frame *frame, struct vb2_buffer *vb); +void mdp_set_dst_config(struct img_output *out, + struct mdp_frame *frame, struct vb2_buffer *vb); +int mdp_frameparam_init(struct mdp_frameparam *param); + +#endif /* __MTK_MDP3_REGS_H__ */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c new file mode 100644 index 000000000000..9f5844385c8f --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#include +#include +#include "mtk-mdp3-vpu.h" +#include "mtk-mdp3-core.h" + +#define MDP_VPU_MESSAGE_TIMEOUT 500U +#define vpu_alloc_size 0x600000 + +static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu) +{ + return container_of(vpu, struct mdp_dev, vpu); +} + +static int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu) +{ + if (vpu->work && vpu->work_addr) + return 0; + + vpu->work = dma_alloc_coherent(scp_get_device(vpu->scp), vpu_alloc_size, + &vpu->work_addr, GFP_KERNEL); + + if (!vpu->work) + return -ENOMEM; + else + return 0; +} + +void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu) +{ + if (vpu->work && vpu->work_addr) + dma_free_coherent(scp_get_device(vpu->scp), vpu_alloc_size, + vpu->work, vpu->work_addr); +} + +static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len, + void *priv) +{ + struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data; + struct mdp_vpu_dev *vpu = + (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; + + if (!vpu->work_size) + vpu->work_size = msg->work_size; + + vpu->status = msg->status; + complete(&vpu->ipi_acked); +} + +static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len, + void *priv) +{ + struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data; + struct mdp_vpu_dev *vpu = + (struct mdp_vpu_dev *)(unsigned long)msg->drv_data; + + vpu->status = msg->status; + complete(&vpu->ipi_acked); +} + +static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len, + void *priv) +{ + struct img_sw_addr *addr = (struct img_sw_addr *)data; + struct img_ipi_frameparam *param = + (struct img_ipi_frameparam *)(unsigned long)addr->va; + struct mdp_vpu_ctx *ctx = + (struct mdp_vpu_ctx *)(unsigned long)param->drv_data; + + if (param->state) { + struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev); + + dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state); + } + ctx->vpu_dev->status = param->state; + complete(&ctx->vpu_dev->ipi_acked); +} + +int mdp_vpu_register(struct mdp_dev *mdp) +{ + int err; + struct mtk_scp *scp = mdp->scp; + struct device *dev = &mdp->pdev->dev; + + err = scp_ipi_register(scp, SCP_IPI_MDP_INIT, + mdp_vpu_ipi_handle_init_ack, NULL); + if (err) { + dev_err(dev, "scp_ipi_register failed %d\n", err); + goto err_ipi_init; + } + err = scp_ipi_register(scp, SCP_IPI_MDP_DEINIT, + mdp_vpu_ipi_handle_deinit_ack, NULL); + if (err) { + dev_err(dev, "scp_ipi_register failed %d\n", err); + goto err_ipi_deinit; + } + err = scp_ipi_register(scp, SCP_IPI_MDP_FRAME, + mdp_vpu_ipi_handle_frame_ack, NULL); + if (err) { + dev_err(dev, "scp_ipi_register failed %d\n", err); + goto err_ipi_frame; + } + return 0; + +err_ipi_frame: + scp_ipi_unregister(scp, SCP_IPI_MDP_DEINIT); +err_ipi_deinit: + scp_ipi_unregister(scp, SCP_IPI_MDP_INIT); +err_ipi_init: + + return err; +} + +void mdp_vpu_unregister(struct mdp_dev *mdp) +{ + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_INIT); + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_DEINIT); + scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_FRAME); +} + +static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id, + void *buf, unsigned int len) +{ + struct mdp_dev *mdp = vpu_to_mdp(vpu); + unsigned int t = MDP_VPU_MESSAGE_TIMEOUT; + int ret; + + if (!vpu->scp) { + dev_dbg(&mdp->pdev->dev, "vpu scp is NULL"); + return -EINVAL; + } + ret = scp_ipi_send(vpu->scp, id, buf, len, 2000); + + if (ret) { + dev_err(&mdp->pdev->dev, "scp_ipi_send failed %d\n", ret); + return -EPERM; + } + ret = wait_for_completion_timeout(&vpu->ipi_acked, + msecs_to_jiffies(t)); + if (!ret) + ret = -ETIME; + else if (vpu->status) + ret = -EINVAL; + else + ret = 0; + return ret; +} + +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, + struct mutex *lock) +{ + struct mdp_ipi_init_msg msg = { + .drv_data = (unsigned long)vpu, + }; + size_t mem_size; + phys_addr_t pool; + const size_t pool_size = sizeof(struct mdp_config_pool); + struct mdp_dev *mdp = vpu_to_mdp(vpu); + int err; + + init_completion(&vpu->ipi_acked); + vpu->scp = scp; + vpu->lock = lock; + vpu->work_size = 0; + err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); + if (err) + goto err_work_size; + /* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */ + + mem_size = vpu_alloc_size; + if (mdp_vpu_shared_mem_alloc(vpu)) { + dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); + goto err_mem_alloc; + } + + pool = ALIGN((uintptr_t)vpu->work + vpu->work_size, 8); + if (pool + pool_size - (uintptr_t)vpu->work > mem_size) { + dev_err(&mdp->pdev->dev, + "VPU memory insufficient: %zx + %zx > %zx", + vpu->work_size, pool_size, mem_size); + err = -ENOMEM; + goto err_mem_size; + } + + dev_dbg(&mdp->pdev->dev, + "VPU work:%pK pa:%pad sz:%zx pool:%pa sz:%zx (mem sz:%zx)", + vpu->work, &vpu->work_addr, vpu->work_size, + &pool, pool_size, mem_size); + vpu->pool = (struct mdp_config_pool *)(uintptr_t)pool; + msg.work_addr = vpu->work_addr; + msg.work_size = vpu->work_size; + err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg)); + if (err) + goto err_work_size; + + memset(vpu->pool, 0, sizeof(*vpu->pool)); + return 0; + +err_work_size: + switch (vpu->status) { + case -MDP_IPI_EBUSY: + err = -EBUSY; + break; + case -MDP_IPI_ENOMEM: + err = -ENOSPC; /* -ENOMEM */ + break; + } + return err; +err_mem_size: +err_mem_alloc: + return err; +} + +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu) +{ + struct mdp_ipi_deinit_msg msg = { + .drv_data = (unsigned long)vpu, + .work_addr = vpu->work_addr, + }; + + return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg)); +} + +static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu, + enum mdp_config_id id, uint32_t *addr) +{ + struct img_config *config; + + if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) + return ERR_PTR(-EINVAL); + + mutex_lock(vpu->lock); + vpu->pool->cfg_count[id]++; + config = &vpu->pool->configs[id]; + *addr = vpu->work_addr + ((uintptr_t)config - (uintptr_t)vpu->work); + mutex_unlock(vpu->lock); + + return config; +} + +static int mdp_config_put(struct mdp_vpu_dev *vpu, + enum mdp_config_id id, + const struct img_config *config) +{ + int err = 0; + + if (id < 0 || id >= MDP_CONFIG_POOL_SIZE) + return -EINVAL; + if (vpu->lock) + mutex_lock(vpu->lock); + if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id]) + err = -EINVAL; + else + vpu->pool->cfg_count[id]--; + if (vpu->lock) + mutex_unlock(vpu->lock); + return err; +} + +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, + enum mdp_config_id id) +{ + ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr); + if (IS_ERR(ctx->config)) { + int err = PTR_ERR(ctx->config); + + ctx->config = NULL; + return err; + } + ctx->config_id = id; + ctx->vpu_dev = vpu; + return 0; +} + +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx) +{ + int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config); + + ctx->config_id = 0; + ctx->config = NULL; + ctx->inst_addr = 0; + return err; +} + +int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param) +{ + struct mdp_vpu_dev *vpu = ctx->vpu_dev; + struct mdp_dev *mdp = vpu_to_mdp(vpu); + struct img_sw_addr addr; + + if (!ctx->vpu_dev->work || !ctx->vpu_dev->work_addr) { + if (mdp_vpu_shared_mem_alloc(vpu)) { + dev_err(&mdp->pdev->dev, "VPU memory alloc fail!"); + return -ENOMEM; + } + } + memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size); + memset(ctx->config, 0, sizeof(*ctx->config)); + param->config_data.va = (unsigned long)ctx->config; + param->config_data.pa = ctx->inst_addr; + param->drv_data = (unsigned long)ctx; + + memcpy((void *)ctx->vpu_dev->work, param, sizeof(*param)); + addr.pa = ctx->vpu_dev->work_addr; + addr.va = (uintptr_t)ctx->vpu_dev->work; + return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME, + &addr, sizeof(addr)); +} diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h new file mode 100644 index 000000000000..244b3a32d689 --- /dev/null +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Ping-Hsun Wu + */ + +#ifndef __MTK_MDP3_VPU_H__ +#define __MTK_MDP3_VPU_H__ + +#include +#include "mtk-img-ipi.h" + +enum mdp_ipi_result { + MDP_IPI_SUCCESS = 0, + MDP_IPI_ENOMEM = 12, + MDP_IPI_EBUSY = 16, + MDP_IPI_EINVAL = 22, + MDP_IPI_EMINST = 24, + MDP_IPI_ERANGE = 34, + MDP_IPI_NR_ERRNO, + + MDP_IPI_EOTHER = MDP_IPI_NR_ERRNO, + MDP_IPI_PATH_CANT_MERGE, + MDP_IPI_OP_FAIL, +}; + +struct mdp_ipi_init_msg { + u32 status; + u64 drv_data; + u32 work_addr; /* [in] working buffer address */ + u32 work_size; /* [in] working buffer size */ +} __packed; + +struct mdp_ipi_deinit_msg { + u32 status; + u64 drv_data; + u32 work_addr; +} __packed; + +enum mdp_config_id { + MDP_DEV_M2M = 0, + MDP_CONFIG_POOL_SIZE /* ALWAYS keep at the end */ +}; + +struct mdp_config_pool { + u64 cfg_count[MDP_CONFIG_POOL_SIZE]; + struct img_config configs[MDP_CONFIG_POOL_SIZE]; +}; + +struct mdp_vpu_dev { + /* synchronization protect for accessing vpu working buffer info */ + struct mutex *lock; + struct mtk_scp *scp; + struct completion ipi_acked; + void *work; + dma_addr_t work_addr; + size_t work_size; + struct mdp_config_pool *pool; + u32 status; +}; + +struct mdp_vpu_ctx { + struct mdp_vpu_dev *vpu_dev; + u32 config_id; + struct img_config *config; + u32 inst_addr; +}; + +void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu); +int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp, + struct mutex *lock /* for sync */); +int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu); +int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu, + enum mdp_config_id id); +int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx); +int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param); + +#endif /* __MTK_MDP3_VPU_H__ */ -- cgit v1.3-14-g43fede From fbb6c848dd89786fe24856ee6b5e773910ded29c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 18 Jul 2022 23:41:21 +0200 Subject: media: destage Hantro VPU driver The Hantro mainline driver has been used in production since several years and was only kept as a staging driver due the stateless CODEC controls. Now that all the stateless CODEC controls have been moved out of staging, graduate the driver as well. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/verisilicon/Kconfig | 54 + drivers/media/platform/verisilicon/Makefile | 38 + drivers/media/platform/verisilicon/hantro.h | 485 +++++++++ drivers/media/platform/verisilicon/hantro_drv.c | 1146 ++++++++++++++++++++ drivers/media/platform/verisilicon/hantro_g1.c | 39 + .../platform/verisilicon/hantro_g1_h264_dec.c | 284 +++++ .../platform/verisilicon/hantro_g1_mpeg2_dec.c | 240 ++++ .../media/platform/verisilicon/hantro_g1_regs.h | 356 ++++++ .../media/platform/verisilicon/hantro_g1_vp8_dec.c | 511 +++++++++ drivers/media/platform/verisilicon/hantro_g2.c | 44 + .../platform/verisilicon/hantro_g2_hevc_dec.c | 629 +++++++++++ .../media/platform/verisilicon/hantro_g2_regs.h | 325 ++++++ .../media/platform/verisilicon/hantro_g2_vp9_dec.c | 1014 +++++++++++++++++ .../platform/verisilicon/hantro_h1_jpeg_enc.c | 166 +++ .../media/platform/verisilicon/hantro_h1_regs.h | 154 +++ drivers/media/platform/verisilicon/hantro_h264.c | 521 +++++++++ drivers/media/platform/verisilicon/hantro_hevc.c | 284 +++++ drivers/media/platform/verisilicon/hantro_hw.h | 441 ++++++++ drivers/media/platform/verisilicon/hantro_jpeg.c | 348 ++++++ drivers/media/platform/verisilicon/hantro_jpeg.h | 15 + drivers/media/platform/verisilicon/hantro_mpeg2.c | 61 ++ .../media/platform/verisilicon/hantro_postproc.c | 279 +++++ drivers/media/platform/verisilicon/hantro_v4l2.c | 990 +++++++++++++++++ drivers/media/platform/verisilicon/hantro_v4l2.h | 29 + drivers/media/platform/verisilicon/hantro_vp8.c | 201 ++++ drivers/media/platform/verisilicon/hantro_vp9.c | 240 ++++ drivers/media/platform/verisilicon/hantro_vp9.h | 102 ++ drivers/media/platform/verisilicon/imx8m_vpu_hw.c | 373 +++++++ .../verisilicon/rockchip_vpu2_hw_h264_dec.c | 491 +++++++++ .../verisilicon/rockchip_vpu2_hw_jpeg_enc.c | 197 ++++ .../verisilicon/rockchip_vpu2_hw_mpeg2_dec.c | 248 +++++ .../verisilicon/rockchip_vpu2_hw_vp8_dec.c | 600 ++++++++++ .../platform/verisilicon/rockchip_vpu2_regs.h | 600 ++++++++++ .../media/platform/verisilicon/rockchip_vpu_hw.c | 680 ++++++++++++ .../media/platform/verisilicon/sama5d4_vdec_hw.c | 128 +++ drivers/media/platform/verisilicon/sunxi_vpu_hw.c | 129 +++ drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/hantro/Kconfig | 50 - drivers/staging/media/hantro/Makefile | 38 - drivers/staging/media/hantro/TODO | 2 - drivers/staging/media/hantro/hantro.h | 485 --------- drivers/staging/media/hantro/hantro_drv.c | 1146 -------------------- drivers/staging/media/hantro/hantro_g1.c | 39 - drivers/staging/media/hantro/hantro_g1_h264_dec.c | 284 ----- drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c | 240 ---- drivers/staging/media/hantro/hantro_g1_regs.h | 356 ------ drivers/staging/media/hantro/hantro_g1_vp8_dec.c | 511 --------- drivers/staging/media/hantro/hantro_g2.c | 44 - drivers/staging/media/hantro/hantro_g2_hevc_dec.c | 629 ----------- drivers/staging/media/hantro/hantro_g2_regs.h | 325 ------ drivers/staging/media/hantro/hantro_g2_vp9_dec.c | 1014 ----------------- drivers/staging/media/hantro/hantro_h1_jpeg_enc.c | 166 --- drivers/staging/media/hantro/hantro_h1_regs.h | 154 --- drivers/staging/media/hantro/hantro_h264.c | 521 --------- drivers/staging/media/hantro/hantro_hevc.c | 284 ----- drivers/staging/media/hantro/hantro_hw.h | 441 -------- drivers/staging/media/hantro/hantro_jpeg.c | 348 ------ drivers/staging/media/hantro/hantro_jpeg.h | 15 - drivers/staging/media/hantro/hantro_mpeg2.c | 61 -- drivers/staging/media/hantro/hantro_postproc.c | 279 ----- drivers/staging/media/hantro/hantro_v4l2.c | 990 ----------------- drivers/staging/media/hantro/hantro_v4l2.h | 29 - drivers/staging/media/hantro/hantro_vp8.c | 201 ---- drivers/staging/media/hantro/hantro_vp9.c | 240 ---- drivers/staging/media/hantro/hantro_vp9.h | 102 -- drivers/staging/media/hantro/imx8m_vpu_hw.c | 373 ------- .../media/hantro/rockchip_vpu2_hw_h264_dec.c | 491 --------- .../media/hantro/rockchip_vpu2_hw_jpeg_enc.c | 197 ---- .../media/hantro/rockchip_vpu2_hw_mpeg2_dec.c | 248 ----- .../media/hantro/rockchip_vpu2_hw_vp8_dec.c | 600 ---------- drivers/staging/media/hantro/rockchip_vpu2_regs.h | 600 ---------- drivers/staging/media/hantro/rockchip_vpu_hw.c | 680 ------------ drivers/staging/media/hantro/sama5d4_vdec_hw.c | 128 --- drivers/staging/media/hantro/sunxi_vpu_hw.c | 129 --- 78 files changed, 12445 insertions(+), 12444 deletions(-) create mode 100644 drivers/media/platform/verisilicon/Kconfig create mode 100644 drivers/media/platform/verisilicon/Makefile create mode 100644 drivers/media/platform/verisilicon/hantro.h create mode 100644 drivers/media/platform/verisilicon/hantro_drv.c create mode 100644 drivers/media/platform/verisilicon/hantro_g1.c create mode 100644 drivers/media/platform/verisilicon/hantro_g1_h264_dec.c create mode 100644 drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c create mode 100644 drivers/media/platform/verisilicon/hantro_g1_regs.h create mode 100644 drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c create mode 100644 drivers/media/platform/verisilicon/hantro_g2.c create mode 100644 drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c create mode 100644 drivers/media/platform/verisilicon/hantro_g2_regs.h create mode 100644 drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c create mode 100644 drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c create mode 100644 drivers/media/platform/verisilicon/hantro_h1_regs.h create mode 100644 drivers/media/platform/verisilicon/hantro_h264.c create mode 100644 drivers/media/platform/verisilicon/hantro_hevc.c create mode 100644 drivers/media/platform/verisilicon/hantro_hw.h create mode 100644 drivers/media/platform/verisilicon/hantro_jpeg.c create mode 100644 drivers/media/platform/verisilicon/hantro_jpeg.h create mode 100644 drivers/media/platform/verisilicon/hantro_mpeg2.c create mode 100644 drivers/media/platform/verisilicon/hantro_postproc.c create mode 100644 drivers/media/platform/verisilicon/hantro_v4l2.c create mode 100644 drivers/media/platform/verisilicon/hantro_v4l2.h create mode 100644 drivers/media/platform/verisilicon/hantro_vp8.c create mode 100644 drivers/media/platform/verisilicon/hantro_vp9.c create mode 100644 drivers/media/platform/verisilicon/hantro_vp9.h create mode 100644 drivers/media/platform/verisilicon/imx8m_vpu_hw.c create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu2_regs.h create mode 100644 drivers/media/platform/verisilicon/rockchip_vpu_hw.c create mode 100644 drivers/media/platform/verisilicon/sama5d4_vdec_hw.c create mode 100644 drivers/media/platform/verisilicon/sunxi_vpu_hw.c delete mode 100644 drivers/staging/media/hantro/Kconfig delete mode 100644 drivers/staging/media/hantro/Makefile delete mode 100644 drivers/staging/media/hantro/TODO delete mode 100644 drivers/staging/media/hantro/hantro.h delete mode 100644 drivers/staging/media/hantro/hantro_drv.c delete mode 100644 drivers/staging/media/hantro/hantro_g1.c delete mode 100644 drivers/staging/media/hantro/hantro_g1_h264_dec.c delete mode 100644 drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c delete mode 100644 drivers/staging/media/hantro/hantro_g1_regs.h delete mode 100644 drivers/staging/media/hantro/hantro_g1_vp8_dec.c delete mode 100644 drivers/staging/media/hantro/hantro_g2.c delete mode 100644 drivers/staging/media/hantro/hantro_g2_hevc_dec.c delete mode 100644 drivers/staging/media/hantro/hantro_g2_regs.h delete mode 100644 drivers/staging/media/hantro/hantro_g2_vp9_dec.c delete mode 100644 drivers/staging/media/hantro/hantro_h1_jpeg_enc.c delete mode 100644 drivers/staging/media/hantro/hantro_h1_regs.h delete mode 100644 drivers/staging/media/hantro/hantro_h264.c delete mode 100644 drivers/staging/media/hantro/hantro_hevc.c delete mode 100644 drivers/staging/media/hantro/hantro_hw.h delete mode 100644 drivers/staging/media/hantro/hantro_jpeg.c delete mode 100644 drivers/staging/media/hantro/hantro_jpeg.h delete mode 100644 drivers/staging/media/hantro/hantro_mpeg2.c delete mode 100644 drivers/staging/media/hantro/hantro_postproc.c delete mode 100644 drivers/staging/media/hantro/hantro_v4l2.c delete mode 100644 drivers/staging/media/hantro/hantro_v4l2.h delete mode 100644 drivers/staging/media/hantro/hantro_vp8.c delete mode 100644 drivers/staging/media/hantro/hantro_vp9.c delete mode 100644 drivers/staging/media/hantro/hantro_vp9.h delete mode 100644 drivers/staging/media/hantro/imx8m_vpu_hw.c delete mode 100644 drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c delete mode 100644 drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c delete mode 100644 drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c delete mode 100644 drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c delete mode 100644 drivers/staging/media/hantro/rockchip_vpu2_regs.h delete mode 100644 drivers/staging/media/hantro/rockchip_vpu_hw.c delete mode 100644 drivers/staging/media/hantro/sama5d4_vdec_hw.c delete mode 100644 drivers/staging/media/hantro/sunxi_vpu_hw.c diff --git a/MAINTAINERS b/MAINTAINERS index fee641d10847..a58f1fc6dd47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8904,7 +8904,7 @@ S: Maintained F: Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml F: Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml F: Documentation/devicetree/bindings/media/rockchip-vpu.yaml -F: drivers/staging/media/hantro/ +F: drivers/media/platform/verisilicon/ HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f1056ceaf5a8..a9334263fa9b 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -81,6 +81,7 @@ source "drivers/media/platform/samsung/Kconfig" source "drivers/media/platform/st/Kconfig" source "drivers/media/platform/sunxi/Kconfig" source "drivers/media/platform/ti/Kconfig" +source "drivers/media/platform/verisilicon/Kconfig" source "drivers/media/platform/via/Kconfig" source "drivers/media/platform/xilinx/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index a881e97bae95..a91f42024273 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -24,6 +24,7 @@ obj-y += samsung/ obj-y += st/ obj-y += sunxi/ obj-y += ti/ +obj-y += verisilicon/ obj-y += via/ obj-y += xilinx/ diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig new file mode 100644 index 000000000000..e65b836b9d78 --- /dev/null +++ b/drivers/media/platform/verisilicon/Kconfig @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Verisilicon media platform drivers" + +config VIDEO_HANTRO + tristate "Hantro VPU driver" + depends on ARCH_MXC || ARCH_ROCKCHIP || ARCH_AT91 || ARCH_SUNXI || COMPILE_TEST + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select V4L2_H264 + select V4L2_VP9 + help + Support for the Hantro IP based Video Processing Units present on + Rockchip and NXP i.MX8M SoCs, which accelerate video and image + encoding and decoding. + To compile this driver as a module, choose M here: the module + will be called hantro-vpu. + +config VIDEO_HANTRO_IMX8M + bool "Hantro VPU i.MX8M support" + depends on VIDEO_HANTRO + depends on ARCH_MXC || COMPILE_TEST + default y + help + Enable support for i.MX8M SoCs. + +config VIDEO_HANTRO_SAMA5D4 + bool "Hantro VDEC SAMA5D4 support" + depends on VIDEO_HANTRO + depends on ARCH_AT91 || COMPILE_TEST + default y + help + Enable support for Microchip SAMA5D4 SoCs. + +config VIDEO_HANTRO_ROCKCHIP + bool "Hantro VPU Rockchip support" + depends on VIDEO_HANTRO + depends on ARCH_ROCKCHIP || COMPILE_TEST + default y + help + Enable support for RK3288, RK3328, and RK3399 SoCs. + +config VIDEO_HANTRO_SUNXI + bool "Hantro VPU Allwinner support" + depends on VIDEO_HANTRO + depends on ARCH_SUNXI || COMPILE_TEST + default y + help + Enable support for H6 SoC. diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile new file mode 100644 index 000000000000..ebd5ede7bef7 --- /dev/null +++ b/drivers/media/platform/verisilicon/Makefile @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_HANTRO) += hantro-vpu.o + +hantro-vpu-y += \ + hantro_drv.o \ + hantro_v4l2.o \ + hantro_postproc.o \ + hantro_h1_jpeg_enc.o \ + hantro_g1.o \ + hantro_g1_h264_dec.o \ + hantro_g1_mpeg2_dec.o \ + hantro_g1_vp8_dec.o \ + hantro_g2.o \ + hantro_g2_hevc_dec.o \ + hantro_g2_vp9_dec.o \ + rockchip_vpu2_hw_jpeg_enc.o \ + rockchip_vpu2_hw_h264_dec.o \ + rockchip_vpu2_hw_mpeg2_dec.o \ + rockchip_vpu2_hw_vp8_dec.o \ + hantro_jpeg.o \ + hantro_h264.o \ + hantro_hevc.o \ + hantro_mpeg2.o \ + hantro_vp8.o \ + hantro_vp9.o + +hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \ + imx8m_vpu_hw.o + +hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \ + sama5d4_vdec_hw.o + +hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ + rockchip_vpu_hw.o + +hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ + sunxi_vpu_hw.o diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h new file mode 100644 index 000000000000..2989ebc631cc --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro.h @@ -0,0 +1,485 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright 2018 Google LLC. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#ifndef HANTRO_H_ +#define HANTRO_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hantro_hw.h" + +struct hantro_ctx; +struct hantro_codec_ops; +struct hantro_postproc_ops; + +#define HANTRO_JPEG_ENCODER BIT(0) +#define HANTRO_ENCODERS 0x0000ffff +#define HANTRO_MPEG2_DECODER BIT(16) +#define HANTRO_VP8_DECODER BIT(17) +#define HANTRO_H264_DECODER BIT(18) +#define HANTRO_HEVC_DECODER BIT(19) +#define HANTRO_VP9_DECODER BIT(20) +#define HANTRO_DECODERS 0xffff0000 + +/** + * struct hantro_irq - irq handler and name + * + * @name: irq name for device tree lookup + * @handler: interrupt handler + */ +struct hantro_irq { + const char *name; + irqreturn_t (*handler)(int irq, void *priv); +}; + +/** + * struct hantro_variant - information about VPU hardware variant + * + * @enc_offset: Offset from VPU base to encoder registers. + * @dec_offset: Offset from VPU base to decoder registers. + * @enc_fmts: Encoder formats. + * @num_enc_fmts: Number of encoder formats. + * @dec_fmts: Decoder formats. + * @num_dec_fmts: Number of decoder formats. + * @postproc_fmts: Post-processor formats. + * @num_postproc_fmts: Number of post-processor formats. + * @postproc_ops: Post-processor ops. + * @codec: Supported codecs + * @codec_ops: Codec ops. + * @init: Initialize hardware, optional. + * @runtime_resume: reenable hardware after power gating, optional. + * @irqs: array of irq names and interrupt handlers + * @num_irqs: number of irqs in the array + * @clk_names: array of clock names + * @num_clocks: number of clocks in the array + * @reg_names: array of register range names + * @num_regs: number of register range names in the array + * @double_buffer: core needs double buffering + * @legacy_regs: core uses legacy register set + * @late_postproc: postproc must be set up at the end of the job + */ +struct hantro_variant { + unsigned int enc_offset; + unsigned int dec_offset; + const struct hantro_fmt *enc_fmts; + unsigned int num_enc_fmts; + const struct hantro_fmt *dec_fmts; + unsigned int num_dec_fmts; + const struct hantro_fmt *postproc_fmts; + unsigned int num_postproc_fmts; + const struct hantro_postproc_ops *postproc_ops; + unsigned int codec; + const struct hantro_codec_ops *codec_ops; + int (*init)(struct hantro_dev *vpu); + int (*runtime_resume)(struct hantro_dev *vpu); + const struct hantro_irq *irqs; + int num_irqs; + const char * const *clk_names; + int num_clocks; + const char * const *reg_names; + int num_regs; + unsigned int double_buffer : 1; + unsigned int legacy_regs : 1; + unsigned int late_postproc : 1; +}; + +/** + * enum hantro_codec_mode - codec operating mode. + * @HANTRO_MODE_NONE: No operating mode. Used for RAW video formats. + * @HANTRO_MODE_JPEG_ENC: JPEG encoder. + * @HANTRO_MODE_H264_DEC: H264 decoder. + * @HANTRO_MODE_MPEG2_DEC: MPEG-2 decoder. + * @HANTRO_MODE_VP8_DEC: VP8 decoder. + * @HANTRO_MODE_HEVC_DEC: HEVC decoder. + * @HANTRO_MODE_VP9_DEC: VP9 decoder. + */ +enum hantro_codec_mode { + HANTRO_MODE_NONE = -1, + HANTRO_MODE_JPEG_ENC, + HANTRO_MODE_H264_DEC, + HANTRO_MODE_MPEG2_DEC, + HANTRO_MODE_VP8_DEC, + HANTRO_MODE_HEVC_DEC, + HANTRO_MODE_VP9_DEC, +}; + +/* + * struct hantro_ctrl - helper type to declare supported controls + * @codec: codec id this control belong to (HANTRO_JPEG_ENCODER, etc.) + * @cfg: control configuration + */ +struct hantro_ctrl { + unsigned int codec; + struct v4l2_ctrl_config cfg; +}; + +/* + * struct hantro_func - Hantro VPU functionality + * + * @id: processing functionality ID (can be + * %MEDIA_ENT_F_PROC_VIDEO_ENCODER or + * %MEDIA_ENT_F_PROC_VIDEO_DECODER) + * @vdev: &struct video_device that exposes the encoder or + * decoder functionality + * @source_pad: &struct media_pad with the source pad. + * @sink: &struct media_entity pointer with the sink entity + * @sink_pad: &struct media_pad with the sink pad. + * @proc: &struct media_entity pointer with the M2M device itself. + * @proc_pads: &struct media_pad with the @proc pads. + * @intf_devnode: &struct media_intf devnode pointer with the interface + * with controls the M2M device. + * + * Contains everything needed to attach the video device to the media device. + */ +struct hantro_func { + unsigned int id; + struct video_device vdev; + struct media_pad source_pad; + struct media_entity sink; + struct media_pad sink_pad; + struct media_entity proc; + struct media_pad proc_pads[2]; + struct media_intf_devnode *intf_devnode; +}; + +static inline struct hantro_func * +hantro_vdev_to_func(struct video_device *vdev) +{ + return container_of(vdev, struct hantro_func, vdev); +} + +/** + * struct hantro_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @m2m_dev: mem2mem device associated to this device. + * @mdev: media device associated to this device. + * @encoder: encoder functionality. + * @decoder: decoder functionality. + * @pdev: Pointer to VPU platform device. + * @dev: Pointer to device for convenient logging using + * dev_ macros. + * @clocks: Array of clock handles. + * @resets: Array of reset handles. + * @reg_bases: Mapped addresses of VPU registers. + * @enc_base: Mapped address of VPU encoder register for convenience. + * @dec_base: Mapped address of VPU decoder register for convenience. + * @ctrl_base: Mapped address of VPU control block. + * @vpu_mutex: Mutex to synchronize V4L2 calls. + * @irqlock: Spinlock to synchronize access to data structures + * shared with interrupt handlers. + * @variant: Hardware variant-specific parameters. + * @watchdog_work: Delayed work for hardware timeout handling. + */ +struct hantro_dev { + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct media_device mdev; + struct hantro_func *encoder; + struct hantro_func *decoder; + struct platform_device *pdev; + struct device *dev; + struct clk_bulk_data *clocks; + struct reset_control *resets; + void __iomem **reg_bases; + void __iomem *enc_base; + void __iomem *dec_base; + void __iomem *ctrl_base; + + struct mutex vpu_mutex; /* video_device lock */ + spinlock_t irqlock; + const struct hantro_variant *variant; + struct delayed_work watchdog_work; +}; + +/** + * struct hantro_ctx - Context (instance) private data. + * + * @dev: VPU driver data to which the context belongs. + * @fh: V4L2 file handler. + * @is_encoder: Decoder or encoder context? + * + * @sequence_cap: Sequence counter for capture queue + * @sequence_out: Sequence counter for output queue + * + * @vpu_src_fmt: Descriptor of active source format. + * @src_fmt: V4L2 pixel format of active source format. + * @vpu_dst_fmt: Descriptor of active destination format. + * @dst_fmt: V4L2 pixel format of active destination format. + * + * @ctrl_handler: Control handler used to register controls. + * @jpeg_quality: User-specified JPEG compression quality. + * @bit_depth: Bit depth of current frame + * + * @codec_ops: Set of operations related to codec mode. + * @postproc: Post-processing context. + * @h264_dec: H.264-decoding context. + * @jpeg_enc: JPEG-encoding context. + * @mpeg2_dec: MPEG-2-decoding context. + * @vp8_dec: VP8-decoding context. + * @hevc_dec: HEVC-decoding context. + * @vp9_dec: VP9-decoding context. + */ +struct hantro_ctx { + struct hantro_dev *dev; + struct v4l2_fh fh; + bool is_encoder; + + u32 sequence_cap; + u32 sequence_out; + + const struct hantro_fmt *vpu_src_fmt; + struct v4l2_pix_format_mplane src_fmt; + const struct hantro_fmt *vpu_dst_fmt; + struct v4l2_pix_format_mplane dst_fmt; + + struct v4l2_ctrl_handler ctrl_handler; + int jpeg_quality; + int bit_depth; + + const struct hantro_codec_ops *codec_ops; + struct hantro_postproc_ctx postproc; + + /* Specific for particular codec modes. */ + union { + struct hantro_h264_dec_hw_ctx h264_dec; + struct hantro_mpeg2_dec_hw_ctx mpeg2_dec; + struct hantro_vp8_dec_hw_ctx vp8_dec; + struct hantro_hevc_dec_hw_ctx hevc_dec; + struct hantro_vp9_dec_hw_ctx vp9_dec; + }; +}; + +/** + * struct hantro_fmt - information about supported video formats. + * @name: Human readable name of the format. + * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. + * @codec_mode: Codec mode related to this format. See + * enum hantro_codec_mode. + * @header_size: Optional header size. Currently used by JPEG encoder. + * @max_depth: Maximum depth, for bitstream formats + * @enc_fmt: Format identifier for encoder registers. + * @frmsize: Supported range of frame sizes (only for bitstream formats). + * @postprocessed: Indicates if this format needs the post-processor. + * @match_depth: Indicates if format bit depth must match video bit depth + */ +struct hantro_fmt { + char *name; + u32 fourcc; + enum hantro_codec_mode codec_mode; + int header_size; + int max_depth; + enum hantro_enc_fmt enc_fmt; + struct v4l2_frmsize_stepwise frmsize; + bool postprocessed; + bool match_depth; +}; + +struct hantro_reg { + u32 base; + u32 shift; + u32 mask; +}; + +struct hantro_postproc_regs { + struct hantro_reg pipeline_en; + struct hantro_reg max_burst; + struct hantro_reg clk_gate; + struct hantro_reg out_swap32; + struct hantro_reg out_endian; + struct hantro_reg out_luma_base; + struct hantro_reg input_width; + struct hantro_reg input_height; + struct hantro_reg output_width; + struct hantro_reg output_height; + struct hantro_reg input_fmt; + struct hantro_reg output_fmt; + struct hantro_reg orig_width; + struct hantro_reg display_width; +}; + +struct hantro_vp9_decoded_buffer_info { + /* Info needed when the decoded frame serves as a reference frame. */ + unsigned short width; + unsigned short height; + u32 bit_depth : 4; +}; + +struct hantro_decoded_buffer { + /* Must be the first field in this struct. */ + struct v4l2_m2m_buffer base; + + union { + struct hantro_vp9_decoded_buffer_info vp9; + }; +}; + +/* Logging helpers */ + +/** + * DOC: hantro_debug: Module parameter to control level of debugging messages. + * + * Level of debugging messages can be controlled by bits of + * module parameter called "debug". Meaning of particular + * bits is as follows: + * + * bit 0 - global information: mode, size, init, release + * bit 1 - each run start/result information + * bit 2 - contents of small controls from userspace + * bit 3 - contents of big controls from userspace + * bit 4 - detail fmt, ctrl, buffer q/dq information + * bit 5 - detail function enter/leave trace information + * bit 6 - register write/read information + */ +extern int hantro_debug; + +#define vpu_debug(level, fmt, args...) \ + do { \ + if (hantro_debug & BIT(level)) \ + pr_info("%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + } while (0) + +#define vpu_err(fmt, args...) \ + pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) + +/* Structure access helpers. */ +static inline struct hantro_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct hantro_ctx, fh); +} + +/* Register accessors. */ +static inline void vepu_write_relaxed(struct hantro_dev *vpu, + u32 val, u32 reg) +{ + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + writel_relaxed(val, vpu->enc_base + reg); +} + +static inline void vepu_write(struct hantro_dev *vpu, u32 val, u32 reg) +{ + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + writel(val, vpu->enc_base + reg); +} + +static inline u32 vepu_read(struct hantro_dev *vpu, u32 reg) +{ + u32 val = readl(vpu->enc_base + reg); + + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + return val; +} + +static inline void vdpu_write_relaxed(struct hantro_dev *vpu, + u32 val, u32 reg) +{ + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + writel_relaxed(val, vpu->dec_base + reg); +} + +static inline void vdpu_write(struct hantro_dev *vpu, u32 val, u32 reg) +{ + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + writel(val, vpu->dec_base + reg); +} + +static inline void hantro_write_addr(struct hantro_dev *vpu, + unsigned long offset, + dma_addr_t addr) +{ + vdpu_write(vpu, addr & 0xffffffff, offset); +} + +static inline u32 vdpu_read(struct hantro_dev *vpu, u32 reg) +{ + u32 val = readl(vpu->dec_base + reg); + + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + return val; +} + +static inline u32 vdpu_read_mask(struct hantro_dev *vpu, + const struct hantro_reg *reg, + u32 val) +{ + u32 v; + + v = vdpu_read(vpu, reg->base); + v &= ~(reg->mask << reg->shift); + v |= ((val & reg->mask) << reg->shift); + return v; +} + +static inline void hantro_reg_write(struct hantro_dev *vpu, + const struct hantro_reg *reg, + u32 val) +{ + vdpu_write_relaxed(vpu, vdpu_read_mask(vpu, reg, val), reg->base); +} + +static inline void hantro_reg_write_s(struct hantro_dev *vpu, + const struct hantro_reg *reg, + u32 val) +{ + vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); +} + +void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); +dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); + +static inline struct vb2_v4l2_buffer * +hantro_get_src_buf(struct hantro_ctx *ctx) +{ + return v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); +} + +static inline struct vb2_v4l2_buffer * +hantro_get_dst_buf(struct hantro_ctx *ctx) +{ + return v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); +} + +bool hantro_needs_postproc(const struct hantro_ctx *ctx, + const struct hantro_fmt *fmt); + +static inline dma_addr_t +hantro_get_dec_buf_addr(struct hantro_ctx *ctx, struct vb2_buffer *vb) +{ + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + return ctx->postproc.dec_q[vb->index].dma; + return vb2_dma_contig_plane_dma_addr(vb, 0); +} + +static inline struct hantro_decoded_buffer * +vb2_to_hantro_decoded_buf(struct vb2_buffer *buf) +{ + return container_of(buf, struct hantro_decoded_buffer, base.vb.vb2_buf); +} + +void hantro_postproc_disable(struct hantro_ctx *ctx); +void hantro_postproc_enable(struct hantro_ctx *ctx); +void hantro_postproc_free(struct hantro_ctx *ctx); +int hantro_postproc_alloc(struct hantro_ctx *ctx); +int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, + struct v4l2_frmsizeenum *fsize); + +#endif /* HANTRO_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c new file mode 100644 index 000000000000..2036f72eeb4a --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -0,0 +1,1146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Collabora, Ltd. + * Copyright 2018 Google LLC. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hantro_v4l2.h" +#include "hantro.h" +#include "hantro_hw.h" + +#define DRIVER_NAME "hantro-vpu" + +int hantro_debug; +module_param_named(debug, hantro_debug, int, 0644); +MODULE_PARM_DESC(debug, + "Debug level - higher value produces more verbose messages"); + +void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, id); + return ctrl ? ctrl->p_cur.p : NULL; +} + +dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) +{ + struct vb2_queue *q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); + struct vb2_buffer *buf; + + buf = vb2_find_buffer(q, ts); + if (!buf) + return 0; + return hantro_get_dec_buf_addr(ctx, buf); +} + +static const struct v4l2_event hantro_eos_event = { + .type = V4L2_EVENT_EOS +}; + +static void hantro_job_finish_no_pm(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + enum vb2_buffer_state result) +{ + struct vb2_v4l2_buffer *src, *dst; + + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + if (WARN_ON(!src)) + return; + if (WARN_ON(!dst)) + return; + + src->sequence = ctx->sequence_out++; + dst->sequence = ctx->sequence_cap++; + + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src)) { + dst->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + } + + v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, + result); +} + +static void hantro_job_finish(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + enum vb2_buffer_state result) +{ + pm_runtime_mark_last_busy(vpu->dev); + pm_runtime_put_autosuspend(vpu->dev); + + clk_bulk_disable(vpu->variant->num_clocks, vpu->clocks); + + hantro_job_finish_no_pm(vpu, ctx, result); +} + +void hantro_irq_done(struct hantro_dev *vpu, + enum vb2_buffer_state result) +{ + struct hantro_ctx *ctx = + v4l2_m2m_get_curr_priv(vpu->m2m_dev); + + /* + * If cancel_delayed_work returns false + * the timeout expired. The watchdog is running, + * and will take care of finishing the job. + */ + if (cancel_delayed_work(&vpu->watchdog_work)) { + if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) + ctx->codec_ops->done(ctx); + hantro_job_finish(vpu, ctx, result); + } +} + +void hantro_watchdog(struct work_struct *work) +{ + struct hantro_dev *vpu; + struct hantro_ctx *ctx; + + vpu = container_of(to_delayed_work(work), + struct hantro_dev, watchdog_work); + ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev); + if (ctx) { + vpu_err("frame processing timed out!\n"); + ctx->codec_ops->reset(ctx); + hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); + } +} + +void hantro_start_prepare_run(struct hantro_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf; + + src_buf = hantro_get_src_buf(ctx); + v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + if (!ctx->is_encoder && !ctx->dev->variant->late_postproc) { + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + hantro_postproc_enable(ctx); + else + hantro_postproc_disable(ctx); + } +} + +void hantro_end_prepare_run(struct hantro_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf; + + if (!ctx->is_encoder && ctx->dev->variant->late_postproc) { + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + hantro_postproc_enable(ctx); + else + hantro_postproc_disable(ctx); + } + + src_buf = hantro_get_src_buf(ctx); + v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + /* Kick the watchdog. */ + schedule_delayed_work(&ctx->dev->watchdog_work, + msecs_to_jiffies(2000)); +} + +static void device_run(void *priv) +{ + struct hantro_ctx *ctx = priv; + struct vb2_v4l2_buffer *src, *dst; + int ret; + + src = hantro_get_src_buf(ctx); + dst = hantro_get_dst_buf(ctx); + + ret = pm_runtime_resume_and_get(ctx->dev->dev); + if (ret < 0) + goto err_cancel_job; + + ret = clk_bulk_enable(ctx->dev->variant->num_clocks, ctx->dev->clocks); + if (ret) + goto err_cancel_job; + + v4l2_m2m_buf_copy_metadata(src, dst, true); + + if (ctx->codec_ops->run(ctx)) + goto err_cancel_job; + + return; + +err_cancel_job: + hantro_job_finish_no_pm(ctx->dev, ctx, VB2_BUF_STATE_ERROR); +} + +static const struct v4l2_m2m_ops vpu_m2m_ops = { + .device_run = device_run, +}; + +static int +queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct hantro_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->ops = &hantro_queue_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + /* + * Driver does mostly sequential access, so sacrifice TLB efficiency + * for faster allocation. Also, no CPU access on the source queue, + * so no kernel mapping needed. + */ + src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | + DMA_ATTR_NO_KERNEL_MAPPING; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->vpu_mutex; + src_vq->dev = ctx->dev->v4l2_dev.dev; + src_vq->supports_requests = true; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->bidirectional = true; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; + /* + * The Kernel needs access to the JPEG destination buffer for the + * JPEG encoder to fill in the JPEG headers. + */ + if (!ctx->is_encoder) + dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->ops = &hantro_queue_ops; + dst_vq->buf_struct_size = sizeof(struct hantro_decoded_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->vpu_mutex; + dst_vq->dev = ctx->dev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) { + const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; + + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) { + const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame; + + /* We only support profile 0 */ + if (dec_params->profile != 0) + return -EINVAL; + } + return 0; +} + +static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hantro_ctx *ctx; + + ctx = container_of(ctrl->handler, + struct hantro_ctx, ctrl_handler); + + vpu_debug(1, "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->jpeg_quality = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int hantro_vp9_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hantro_ctx *ctx; + + ctx = container_of(ctrl->handler, + struct hantro_ctx, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_STATELESS_VP9_FRAME: + ctx->bit_depth = ctrl->p_new.p_vp9_frame->bit_depth; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops hantro_ctrl_ops = { + .try_ctrl = hantro_try_ctrl, +}; + +static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { + .s_ctrl = hantro_jpeg_s_ctrl, +}; + +static const struct v4l2_ctrl_ops hantro_vp9_ctrl_ops = { + .s_ctrl = hantro_vp9_s_ctrl, +}; + +#define HANTRO_JPEG_ACTIVE_MARKERS (V4L2_JPEG_ACTIVE_MARKER_APP0 | \ + V4L2_JPEG_ACTIVE_MARKER_COM | \ + V4L2_JPEG_ACTIVE_MARKER_DQT | \ + V4L2_JPEG_ACTIVE_MARKER_DHT) + +static const struct hantro_ctrl controls[] = { + { + .codec = HANTRO_JPEG_ENCODER, + .cfg = { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .min = 5, + .max = 100, + .step = 1, + .def = 50, + .ops = &hantro_jpeg_ctrl_ops, + }, + }, { + .codec = HANTRO_JPEG_ENCODER, + .cfg = { + .id = V4L2_CID_JPEG_ACTIVE_MARKER, + .max = HANTRO_JPEG_ACTIVE_MARKERS, + .def = HANTRO_JPEG_ACTIVE_MARKERS, + /* + * Changing the set of active markers/segments also + * messes up the alignment of the JPEG header, which + * is needed to allow the hardware to write directly + * to the output buffer. Implementing this introduces + * a lot of complexity for little gain, as the markers + * enabled is already the minimum required set. + */ + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, + }, { + .codec = HANTRO_MPEG2_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, + }, + }, { + .codec = HANTRO_MPEG2_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_MPEG2_PICTURE, + }, + }, { + .codec = HANTRO_MPEG2_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, + }, + }, { + .codec = HANTRO_VP8_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_VP8_FRAME, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_SPS, + .ops = &hantro_ctrl_ops, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_PPS, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_H264_START_CODE, + .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + } + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_START_CODE, + .min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_SPS, + .ops = &hantro_ctrl_ops, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_PPS, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + }, + }, { + .codec = HANTRO_HEVC_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }, + }, { + .codec = HANTRO_VP9_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_VP9_FRAME, + .ops = &hantro_vp9_ctrl_ops, + }, + }, { + .codec = HANTRO_VP9_DECODER, + .cfg = { + .id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, + }, + }, +}; + +static int hantro_ctrls_setup(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + int allowed_codecs) +{ + int i, num_ctrls = ARRAY_SIZE(controls); + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls); + + for (i = 0; i < num_ctrls; i++) { + if (!(allowed_codecs & controls[i].codec)) + continue; + + v4l2_ctrl_new_custom(&ctx->ctrl_handler, + &controls[i].cfg, NULL); + if (ctx->ctrl_handler.error) { + vpu_err("Adding control (%d) failed %d\n", + controls[i].cfg.id, + ctx->ctrl_handler.error); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ctx->ctrl_handler.error; + } + } + return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); +} + +/* + * V4L2 file operations. + */ + +static int hantro_open(struct file *filp) +{ + struct hantro_dev *vpu = video_drvdata(filp); + struct video_device *vdev = video_devdata(filp); + struct hantro_func *func = hantro_vdev_to_func(vdev); + struct hantro_ctx *ctx; + int allowed_codecs, ret; + + /* + * We do not need any extra locking here, because we operate only + * on local data here, except reading few fields from dev, which + * do not change through device's lifetime (which is guaranteed by + * reference on module from open()) and V4L2 internal objects (such + * as vdev and ctx->fh), which have proper locking done in respective + * helper functions used here. + */ + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = vpu; + if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { + allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; + ctx->is_encoder = true; + } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { + allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; + ctx->is_encoder = false; + } else { + ret = -ENODEV; + goto err_ctx_free; + } + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_ctx_free; + } + + v4l2_fh_init(&ctx->fh, vdev); + filp->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + hantro_reset_fmts(ctx); + + ret = hantro_ctrls_setup(vpu, ctx, allowed_codecs); + if (ret) { + vpu_err("Failed to set up controls\n"); + goto err_fh_free; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + return 0; + +err_fh_free: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); +err_ctx_free: + kfree(ctx); + return ret; +} + +static int hantro_release(struct file *filp) +{ + struct hantro_ctx *ctx = + container_of(filp->private_data, struct hantro_ctx, fh); + + /* + * No need for extra locking because this was the last reference + * to this file. + */ + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations hantro_fops = { + .owner = THIS_MODULE, + .open = hantro_open, + .release = hantro_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct of_device_id of_hantro_match[] = { +#ifdef CONFIG_VIDEO_HANTRO_ROCKCHIP + { .compatible = "rockchip,px30-vpu", .data = &px30_vpu_variant, }, + { .compatible = "rockchip,rk3036-vpu", .data = &rk3036_vpu_variant, }, + { .compatible = "rockchip,rk3066-vpu", .data = &rk3066_vpu_variant, }, + { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, }, + { .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, }, + { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, + { .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, }, + { .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, }, +#endif +#ifdef CONFIG_VIDEO_HANTRO_IMX8M + { .compatible = "nxp,imx8mm-vpu-g1", .data = &imx8mm_vpu_g1_variant, }, + { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, + { .compatible = "nxp,imx8mq-vpu-g1", .data = &imx8mq_vpu_g1_variant }, + { .compatible = "nxp,imx8mq-vpu-g2", .data = &imx8mq_vpu_g2_variant }, +#endif +#ifdef CONFIG_VIDEO_HANTRO_SAMA5D4 + { .compatible = "microchip,sama5d4-vdec", .data = &sama5d4_vdec_variant, }, +#endif +#ifdef CONFIG_VIDEO_HANTRO_SUNXI + { .compatible = "allwinner,sun50i-h6-vpu-g2", .data = &sunxi_vpu_variant, }, +#endif + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_hantro_match); + +static int hantro_register_entity(struct media_device *mdev, + struct media_entity *entity, + const char *entity_name, + struct media_pad *pads, int num_pads, + int function, struct video_device *vdev) +{ + char *name; + int ret; + + entity->obj_type = MEDIA_ENTITY_TYPE_BASE; + if (function == MEDIA_ENT_F_IO_V4L) { + entity->info.dev.major = VIDEO_MAJOR; + entity->info.dev.minor = vdev->minor; + } + + name = devm_kasprintf(mdev->dev, GFP_KERNEL, "%s-%s", vdev->name, + entity_name); + if (!name) + return -ENOMEM; + + entity->name = name; + entity->function = function; + + ret = media_entity_pads_init(entity, num_pads, pads); + if (ret) + return ret; + + ret = media_device_register_entity(mdev, entity); + if (ret) + return ret; + + return 0; +} + +static int hantro_attach_func(struct hantro_dev *vpu, + struct hantro_func *func) +{ + struct media_device *mdev = &vpu->mdev; + struct media_link *link; + int ret; + + /* Create the three encoder entities with their pads */ + func->source_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = hantro_register_entity(mdev, &func->vdev.entity, "source", + &func->source_pad, 1, MEDIA_ENT_F_IO_V4L, + &func->vdev); + if (ret) + return ret; + + func->proc_pads[0].flags = MEDIA_PAD_FL_SINK; + func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE; + ret = hantro_register_entity(mdev, &func->proc, "proc", + func->proc_pads, 2, func->id, + &func->vdev); + if (ret) + goto err_rel_entity0; + + func->sink_pad.flags = MEDIA_PAD_FL_SINK; + ret = hantro_register_entity(mdev, &func->sink, "sink", + &func->sink_pad, 1, MEDIA_ENT_F_IO_V4L, + &func->vdev); + if (ret) + goto err_rel_entity1; + + /* Connect the three entities */ + ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rel_entity2; + + ret = media_create_pad_link(&func->proc, 1, &func->sink, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rm_links0; + + /* Create video interface */ + func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO, + 0, VIDEO_MAJOR, + func->vdev.minor); + if (!func->intf_devnode) { + ret = -ENOMEM; + goto err_rm_links1; + } + + /* Connect the two DMA engines to the interface */ + link = media_create_intf_link(&func->vdev.entity, + &func->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_devnode; + } + + link = media_create_intf_link(&func->sink, &func->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_devnode; + } + return 0; + +err_rm_devnode: + media_devnode_remove(func->intf_devnode); + +err_rm_links1: + media_entity_remove_links(&func->sink); + +err_rm_links0: + media_entity_remove_links(&func->proc); + media_entity_remove_links(&func->vdev.entity); + +err_rel_entity2: + media_device_unregister_entity(&func->sink); + +err_rel_entity1: + media_device_unregister_entity(&func->proc); + +err_rel_entity0: + media_device_unregister_entity(&func->vdev.entity); + return ret; +} + +static void hantro_detach_func(struct hantro_func *func) +{ + media_devnode_remove(func->intf_devnode); + media_entity_remove_links(&func->sink); + media_entity_remove_links(&func->proc); + media_entity_remove_links(&func->vdev.entity); + media_device_unregister_entity(&func->sink); + media_device_unregister_entity(&func->proc); + media_device_unregister_entity(&func->vdev.entity); +} + +static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) +{ + const struct of_device_id *match; + struct hantro_func *func; + struct video_device *vfd; + int ret; + + match = of_match_node(of_hantro_match, vpu->dev->of_node); + func = devm_kzalloc(vpu->dev, sizeof(*func), GFP_KERNEL); + if (!func) { + v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); + return -ENOMEM; + } + + func->id = funcid; + + vfd = &func->vdev; + vfd->fops = &hantro_fops; + vfd->release = video_device_release_empty; + vfd->lock = &vpu->vpu_mutex; + vfd->v4l2_dev = &vpu->v4l2_dev; + vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + vfd->ioctl_ops = &hantro_ioctl_ops; + snprintf(vfd->name, sizeof(vfd->name), "%s-%s", match->compatible, + funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER ? "enc" : "dec"); + + if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { + vpu->encoder = func; + } else { + vpu->decoder = func; + v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); + } + + video_set_drvdata(vfd, vpu); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); + return ret; + } + + ret = hantro_attach_func(vpu, func); + if (ret) { + v4l2_err(&vpu->v4l2_dev, + "Failed to attach functionality to the media device\n"); + goto err_unreg_dev; + } + + v4l2_info(&vpu->v4l2_dev, "registered %s as /dev/video%d\n", vfd->name, + vfd->num); + + return 0; + +err_unreg_dev: + video_unregister_device(vfd); + return ret; +} + +static int hantro_add_enc_func(struct hantro_dev *vpu) +{ + if (!vpu->variant->enc_fmts) + return 0; + + return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); +} + +static int hantro_add_dec_func(struct hantro_dev *vpu) +{ + if (!vpu->variant->dec_fmts) + return 0; + + return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); +} + +static void hantro_remove_func(struct hantro_dev *vpu, + unsigned int funcid) +{ + struct hantro_func *func; + + if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) + func = vpu->encoder; + else + func = vpu->decoder; + + if (!func) + return; + + hantro_detach_func(func); + video_unregister_device(&func->vdev); +} + +static void hantro_remove_enc_func(struct hantro_dev *vpu) +{ + hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); +} + +static void hantro_remove_dec_func(struct hantro_dev *vpu) +{ + hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); +} + +static const struct media_device_ops hantro_m2m_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static int hantro_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct hantro_dev *vpu; + struct resource *res; + int num_bases; + int i, ret; + + vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); + if (!vpu) + return -ENOMEM; + + vpu->dev = &pdev->dev; + vpu->pdev = pdev; + mutex_init(&vpu->vpu_mutex); + spin_lock_init(&vpu->irqlock); + + match = of_match_node(of_hantro_match, pdev->dev.of_node); + vpu->variant = match->data; + + /* + * Support for nxp,imx8mq-vpu is kept for backwards compatibility + * but it's deprecated. Please update your DTS file to use + * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead. + */ + if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu")) + dev_warn(&pdev->dev, "%s compatible is deprecated\n", + match->compatible); + + INIT_DELAYED_WORK(&vpu->watchdog_work, hantro_watchdog); + + vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks, + sizeof(*vpu->clocks), GFP_KERNEL); + if (!vpu->clocks) + return -ENOMEM; + + if (vpu->variant->num_clocks > 1) { + for (i = 0; i < vpu->variant->num_clocks; i++) + vpu->clocks[i].id = vpu->variant->clk_names[i]; + + ret = devm_clk_bulk_get(&pdev->dev, vpu->variant->num_clocks, + vpu->clocks); + if (ret) + return ret; + } else { + /* + * If the driver has a single clk, chances are there will be no + * actual name in the DT bindings. + */ + vpu->clocks[0].clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(vpu->clocks[0].clk)) + return PTR_ERR(vpu->clocks[0].clk); + } + + vpu->resets = devm_reset_control_array_get(&pdev->dev, false, true); + if (IS_ERR(vpu->resets)) + return PTR_ERR(vpu->resets); + + num_bases = vpu->variant->num_regs ?: 1; + vpu->reg_bases = devm_kcalloc(&pdev->dev, num_bases, + sizeof(*vpu->reg_bases), GFP_KERNEL); + if (!vpu->reg_bases) + return -ENOMEM; + + for (i = 0; i < num_bases; i++) { + res = vpu->variant->reg_names ? + platform_get_resource_byname(vpu->pdev, IORESOURCE_MEM, + vpu->variant->reg_names[i]) : + platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); + vpu->reg_bases[i] = devm_ioremap_resource(vpu->dev, res); + if (IS_ERR(vpu->reg_bases[i])) + return PTR_ERR(vpu->reg_bases[i]); + } + vpu->enc_base = vpu->reg_bases[0] + vpu->variant->enc_offset; + vpu->dec_base = vpu->reg_bases[0] + vpu->variant->dec_offset; + + /** + * TODO: Eventually allow taking advantage of full 64-bit address space. + * Until then we assume the MSB portion of buffers' base addresses is + * always 0 due to this masking operation. + */ + ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(vpu->dev, "Could not set DMA coherent mask.\n"); + return ret; + } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + + for (i = 0; i < vpu->variant->num_irqs; i++) { + const char *irq_name; + int irq; + + if (!vpu->variant->irqs[i].handler) + continue; + + if (vpu->variant->num_irqs > 1) { + irq_name = vpu->variant->irqs[i].name; + irq = platform_get_irq_byname(vpu->pdev, irq_name); + } else { + /* + * If the driver has a single IRQ, chances are there + * will be no actual name in the DT bindings. + */ + irq_name = "default"; + irq = platform_get_irq(vpu->pdev, 0); + } + if (irq <= 0) + return -ENXIO; + + ret = devm_request_irq(vpu->dev, irq, + vpu->variant->irqs[i].handler, 0, + dev_name(vpu->dev), vpu); + if (ret) { + dev_err(vpu->dev, "Could not request %s IRQ.\n", + irq_name); + return ret; + } + } + + if (vpu->variant->init) { + ret = vpu->variant->init(vpu); + if (ret) { + dev_err(&pdev->dev, "Failed to init VPU hardware\n"); + return ret; + } + } + + pm_runtime_set_autosuspend_delay(vpu->dev, 100); + pm_runtime_use_autosuspend(vpu->dev); + pm_runtime_enable(vpu->dev); + + ret = reset_control_deassert(vpu->resets); + if (ret) { + dev_err(&pdev->dev, "Failed to deassert resets\n"); + goto err_pm_disable; + } + + ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks); + if (ret) { + dev_err(&pdev->dev, "Failed to prepare clocks\n"); + goto err_rst_assert; + } + + ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register v4l2 device\n"); + goto err_clk_unprepare; + } + platform_set_drvdata(pdev, vpu); + + vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops); + if (IS_ERR(vpu->m2m_dev)) { + v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(vpu->m2m_dev); + goto err_v4l2_unreg; + } + + vpu->mdev.dev = vpu->dev; + strscpy(vpu->mdev.model, DRIVER_NAME, sizeof(vpu->mdev.model)); + strscpy(vpu->mdev.bus_info, "platform: " DRIVER_NAME, + sizeof(vpu->mdev.bus_info)); + media_device_init(&vpu->mdev); + vpu->mdev.ops = &hantro_m2m_media_ops; + vpu->v4l2_dev.mdev = &vpu->mdev; + + ret = hantro_add_enc_func(vpu); + if (ret) { + dev_err(&pdev->dev, "Failed to register encoder\n"); + goto err_m2m_rel; + } + + ret = hantro_add_dec_func(vpu); + if (ret) { + dev_err(&pdev->dev, "Failed to register decoder\n"); + goto err_rm_enc_func; + } + + ret = media_device_register(&vpu->mdev); + if (ret) { + v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n"); + goto err_rm_dec_func; + } + + return 0; + +err_rm_dec_func: + hantro_remove_dec_func(vpu); +err_rm_enc_func: + hantro_remove_enc_func(vpu); +err_m2m_rel: + media_device_cleanup(&vpu->mdev); + v4l2_m2m_release(vpu->m2m_dev); +err_v4l2_unreg: + v4l2_device_unregister(&vpu->v4l2_dev); +err_clk_unprepare: + clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); +err_rst_assert: + reset_control_assert(vpu->resets); +err_pm_disable: + pm_runtime_dont_use_autosuspend(vpu->dev); + pm_runtime_disable(vpu->dev); + return ret; +} + +static int hantro_remove(struct platform_device *pdev) +{ + struct hantro_dev *vpu = platform_get_drvdata(pdev); + + v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); + + media_device_unregister(&vpu->mdev); + hantro_remove_dec_func(vpu); + hantro_remove_enc_func(vpu); + media_device_cleanup(&vpu->mdev); + v4l2_m2m_release(vpu->m2m_dev); + v4l2_device_unregister(&vpu->v4l2_dev); + clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); + reset_control_assert(vpu->resets); + pm_runtime_dont_use_autosuspend(vpu->dev); + pm_runtime_disable(vpu->dev); + return 0; +} + +#ifdef CONFIG_PM +static int hantro_runtime_resume(struct device *dev) +{ + struct hantro_dev *vpu = dev_get_drvdata(dev); + + if (vpu->variant->runtime_resume) + return vpu->variant->runtime_resume(vpu); + + return 0; +} +#endif + +static const struct dev_pm_ops hantro_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(NULL, hantro_runtime_resume, NULL) +}; + +static struct platform_driver hantro_driver = { + .probe = hantro_probe, + .remove = hantro_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(of_hantro_match), + .pm = &hantro_pm_ops, + }, +}; +module_platform_driver(hantro_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alpha Lin "); +MODULE_AUTHOR("Tomasz Figa "); +MODULE_AUTHOR("Ezequiel Garcia "); +MODULE_DESCRIPTION("Hantro VPU codec driver"); diff --git a/drivers/media/platform/verisilicon/hantro_g1.c b/drivers/media/platform/verisilicon/hantro_g1.c new file mode 100644 index 000000000000..0ab1cee62218 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g1.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Jeffy Chen + * Copyright (C) 2019 Pengutronix, Philipp Zabel + * Copyright (C) 2021 Collabora Ltd, Emil Velikov + */ + +#include "hantro.h" +#include "hantro_g1_regs.h" + +irqreturn_t hantro_g1_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, G1_REG_INTERRUPT); + state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} + +void hantro_g1_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); + vdpu_write(vpu, 1, G1_REG_SOFT_RESET); +} diff --git a/drivers/media/platform/verisilicon/hantro_g1_h264_dec.c b/drivers/media/platform/verisilicon/hantro_g1_h264_dec.c new file mode 100644 index 000000000000..9de7f05eff2a --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g1_h264_dec.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip RK3288 VPU codec driver + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * Herman Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + */ + +#include +#include + +#include + +#include "hantro_g1_regs.h" +#include "hantro_hw.h" +#include "hantro_v4l2.h" + +static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; + const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; + const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; + struct hantro_dev *vpu = ctx->dev; + u32 reg; + + /* Decoder control register 0. */ + reg = G1_REG_DEC_CTRL0_DEC_AXI_AUTO; + if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) + reg |= G1_REG_DEC_CTRL0_SEQ_MBAFF_E; + if (sps->profile_idc > 66) { + reg |= G1_REG_DEC_CTRL0_PICORD_COUNT_E; + if (dec_param->nal_ref_idc) + reg |= G1_REG_DEC_CTRL0_WRITE_MVS_E; + } + + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && + (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || + dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) + reg |= G1_REG_DEC_CTRL0_PIC_INTERLACE_E; + if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) + reg |= G1_REG_DEC_CTRL0_PIC_FIELDMODE_E; + if (!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) + reg |= G1_REG_DEC_CTRL0_PIC_TOPFIELD_E; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); + + /* Decoder control register 1. */ + reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) | + G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | + G1_REG_DEC_CTRL1_REF_FRAMES(sps->max_num_ref_frames); + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); + + /* Decoder control register 2. */ + reg = G1_REG_DEC_CTRL2_CH_QP_OFFSET(pps->chroma_qp_index_offset) | + G1_REG_DEC_CTRL2_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset); + + if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) + reg |= G1_REG_DEC_CTRL2_TYPE1_QUANT_E; + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + reg |= G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); + + /* Decoder control register 3. */ + reg = G1_REG_DEC_CTRL3_START_CODE_E | + G1_REG_DEC_CTRL3_INIT_QP(pps->pic_init_qp_minus26 + 26) | + G1_REG_DEC_CTRL3_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL3); + + /* Decoder control register 4. */ + reg = G1_REG_DEC_CTRL4_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | + G1_REG_DEC_CTRL4_FRAMENUM(dec_param->frame_num) | + G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc); + if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) + reg |= G1_REG_DEC_CTRL4_CABAC_E; + if (sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) + reg |= G1_REG_DEC_CTRL4_DIR_8X8_INFER_E; + if (sps->profile_idc >= 100 && sps->chroma_format_idc == 0) + reg |= G1_REG_DEC_CTRL4_BLACKWHITE_E; + if (pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) + reg |= G1_REG_DEC_CTRL4_WEIGHT_PRED_E; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); + + /* Decoder control register 5. */ + reg = G1_REG_DEC_CTRL5_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | + G1_REG_DEC_CTRL5_IDR_PIC_ID(dec_param->idr_pic_id); + if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) + reg |= G1_REG_DEC_CTRL5_CONST_INTRA_E; + if (pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) + reg |= G1_REG_DEC_CTRL5_FILT_CTRL_PRES; + if (pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) + reg |= G1_REG_DEC_CTRL5_RDPIC_CNT_PRES; + if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) + reg |= G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E; + if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) + reg |= G1_REG_DEC_CTRL5_IDR_PIC_E; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL5); + + /* Decoder control register 6. */ + reg = G1_REG_DEC_CTRL6_PPS_ID(pps->pic_parameter_set_id) | + G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | + G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | + G1_REG_DEC_CTRL6_POC_LENGTH(dec_param->pic_order_cnt_bit_size); + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL6); + + /* Error concealment register. */ + vdpu_write_relaxed(vpu, 0, G1_REG_ERR_CONC); + + /* Prediction filter tap register. */ + vdpu_write_relaxed(vpu, + G1_REG_PRED_FLT_PRED_BC_TAP_0_0(1) | + G1_REG_PRED_FLT_PRED_BC_TAP_0_1(-5 & 0x3ff) | + G1_REG_PRED_FLT_PRED_BC_TAP_0_2(20), + G1_REG_PRED_FLT); + + /* Reference picture buffer control register. */ + vdpu_write_relaxed(vpu, 0, G1_REG_REF_BUF_CTRL); + + /* Reference picture buffer control register 2. */ + vdpu_write_relaxed(vpu, G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(8), + G1_REG_REF_BUF_CTRL2); +} + +static void set_ref(struct hantro_ctx *ctx) +{ + const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; + struct hantro_dev *vpu = ctx->dev; + int reg_num; + u32 reg; + int i; + + vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_valid, G1_REG_VALID_REF); + vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_longterm, G1_REG_LT_REF); + + /* + * Set up reference frame picture numbers. + * + * Each G1_REG_REF_PIC(x) register contains numbers of two + * subsequential reference pictures. + */ + for (i = 0; i < HANTRO_H264_DPB_SIZE; i += 2) { + reg = G1_REG_REF_PIC_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, i)) | + G1_REG_REF_PIC_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, i + 1)); + vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(i / 2)); + } + + b0_reflist = ctx->h264_dec.reflists.b0; + b1_reflist = ctx->h264_dec.reflists.b1; + p_reflist = ctx->h264_dec.reflists.p; + + /* + * Each G1_REG_BD_REF_PIC(x) register contains three entries + * of each forward and backward picture list. + */ + reg_num = 0; + for (i = 0; i < 15; i += 3) { + reg = G1_REG_BD_REF_PIC_BINIT_RLIST_F0(b0_reflist[i].index) | + G1_REG_BD_REF_PIC_BINIT_RLIST_F1(b0_reflist[i + 1].index) | + G1_REG_BD_REF_PIC_BINIT_RLIST_F2(b0_reflist[i + 2].index) | + G1_REG_BD_REF_PIC_BINIT_RLIST_B0(b1_reflist[i].index) | + G1_REG_BD_REF_PIC_BINIT_RLIST_B1(b1_reflist[i + 1].index) | + G1_REG_BD_REF_PIC_BINIT_RLIST_B2(b1_reflist[i + 2].index); + vdpu_write_relaxed(vpu, reg, G1_REG_BD_REF_PIC(reg_num++)); + } + + /* + * G1_REG_BD_P_REF_PIC register contains last entries (index 15) + * of forward and backward reference picture lists and first 4 entries + * of P forward picture list. + */ + reg = G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(b0_reflist[15].index) | + G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(b1_reflist[15].index) | + G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(p_reflist[0].index) | + G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(p_reflist[1].index) | + G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(p_reflist[2].index) | + G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(p_reflist[3].index); + vdpu_write_relaxed(vpu, reg, G1_REG_BD_P_REF_PIC); + + /* + * Each G1_REG_FWD_PIC(x) register contains six consecutive + * entries of P forward picture list, starting from index 4. + */ + reg_num = 0; + for (i = 4; i < HANTRO_H264_DPB_SIZE; i += 6) { + reg = G1_REG_FWD_PIC_PINIT_RLIST_F0(p_reflist[i].index) | + G1_REG_FWD_PIC_PINIT_RLIST_F1(p_reflist[i + 1].index) | + G1_REG_FWD_PIC_PINIT_RLIST_F2(p_reflist[i + 2].index) | + G1_REG_FWD_PIC_PINIT_RLIST_F3(p_reflist[i + 3].index) | + G1_REG_FWD_PIC_PINIT_RLIST_F4(p_reflist[i + 4].index) | + G1_REG_FWD_PIC_PINIT_RLIST_F5(p_reflist[i + 5].index); + vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(reg_num++)); + } + + /* Set up addresses of DPB buffers. */ + for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { + dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); + + vdpu_write_relaxed(vpu, dma_addr, G1_REG_ADDR_REF(i)); + } +} + +static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + struct vb2_v4l2_buffer *dst_buf; + struct hantro_dev *vpu = ctx->dev; + dma_addr_t src_dma, dst_dma; + size_t offset = 0; + + /* Source (stream) buffer. */ + src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + vdpu_write_relaxed(vpu, src_dma, G1_REG_ADDR_STR); + + /* Destination (decoded frame) buffer. */ + dst_buf = hantro_get_dst_buf(ctx); + dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); + /* Adjust dma addr to start at second line for bottom field */ + if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) + offset = ALIGN(ctx->src_fmt.width, MB_DIM); + vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DST); + + /* Higher profiles require DMV buffer appended to reference frames. */ + if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { + unsigned int bytes_per_mb = 384; + + /* DMV buffer for monochrome start directly after Y-plane */ + if (ctrls->sps->profile_idc >= 100 && + ctrls->sps->chroma_format_idc == 0) + bytes_per_mb = 256; + offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * + MB_HEIGHT(ctx->src_fmt.height); + + /* + * DMV buffer is split in two for field encoded frames, + * adjust offset for bottom field + */ + if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) + offset += 32 * MB_WIDTH(ctx->src_fmt.width) * + MB_HEIGHT(ctx->src_fmt.height); + vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DIR_MV); + } + + /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ + vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, G1_REG_ADDR_QTABLE); +} + +int hantro_g1_h264_dec_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf; + int ret; + + /* Prepare the H264 decoder context. */ + ret = hantro_h264_dec_prepare_run(ctx); + if (ret) + return ret; + + /* Configure hardware registers. */ + src_buf = hantro_get_src_buf(ctx); + set_params(ctx, src_buf); + set_ref(ctx); + set_buffers(ctx, src_buf); + + hantro_end_prepare_run(ctx); + + /* Start decoding! */ + vdpu_write_relaxed(vpu, + G1_REG_CONFIG_DEC_AXI_RD_ID(0xffu) | + G1_REG_CONFIG_DEC_TIMEOUT_E | + G1_REG_CONFIG_DEC_OUT_ENDIAN | + G1_REG_CONFIG_DEC_STRENDIAN_E | + G1_REG_CONFIG_DEC_MAX_BURST(16) | + G1_REG_CONFIG_DEC_OUTSWAP32_E | + G1_REG_CONFIG_DEC_INSWAP32_E | + G1_REG_CONFIG_DEC_STRSWAP32_E | + G1_REG_CONFIG_DEC_CLK_GATE_E, + G1_REG_CONFIG); + vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c new file mode 100644 index 000000000000..9aea331e1a3c --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include "hantro.h" +#include "hantro_hw.h" +#include "hantro_g1_regs.h" + +#define G1_SWREG(nr) ((nr) * 4) + +#define G1_REG_RLC_VLC_BASE G1_SWREG(12) +#define G1_REG_DEC_OUT_BASE G1_SWREG(13) +#define G1_REG_REFER0_BASE G1_SWREG(14) +#define G1_REG_REFER1_BASE G1_SWREG(15) +#define G1_REG_REFER2_BASE G1_SWREG(16) +#define G1_REG_REFER3_BASE G1_SWREG(17) +#define G1_REG_QTABLE_BASE G1_SWREG(40) + +#define G1_REG_DEC_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) +#define G1_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(23) : 0) +#define G1_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(22) : 0) +#define G1_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(21) : 0) +#define G1_REG_DEC_INSWAP32_E(v) ((v) ? BIT(20) : 0) +#define G1_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(19) : 0) +#define G1_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(18) : 0) +#define G1_REG_DEC_LATENCY(v) (((v) << 11) & GENMASK(16, 11)) +#define G1_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(10) : 0) +#define G1_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(9) : 0) +#define G1_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(8) : 0) +#define G1_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(6) : 0) +#define G1_REG_DEC_SCMD_DIS(v) ((v) ? BIT(5) : 0) +#define G1_REG_DEC_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) + +#define G1_REG_DEC_MODE(v) (((v) << 28) & GENMASK(31, 28)) +#define G1_REG_RLC_MODE_E(v) ((v) ? BIT(27) : 0) +#define G1_REG_PIC_INTERLACE_E(v) ((v) ? BIT(23) : 0) +#define G1_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(22) : 0) +#define G1_REG_PIC_B_E(v) ((v) ? BIT(21) : 0) +#define G1_REG_PIC_INTER_E(v) ((v) ? BIT(20) : 0) +#define G1_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(19) : 0) +#define G1_REG_FWD_INTERLACE_E(v) ((v) ? BIT(18) : 0) +#define G1_REG_FILTERING_DIS(v) ((v) ? BIT(14) : 0) +#define G1_REG_WRITE_MVS_E(v) ((v) ? BIT(12) : 0) +#define G1_REG_DEC_AXI_WR_ID(v) (((v) << 0) & GENMASK(7, 0)) + +#define G1_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) +#define G1_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) +#define G1_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) +#define G1_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) + +#define G1_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) +#define G1_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) +#define G1_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) +#define G1_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) +#define G1_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) +#define G1_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) + +#define G1_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) +#define G1_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) + +#define G1_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) +#define G1_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) +#define G1_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) +#define G1_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) +#define G1_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) +#define G1_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) +#define G1_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) + +#define G1_REG_STARTMB_X(v) (((v) << 23) & GENMASK(31, 23)) +#define G1_REG_STARTMB_Y(v) (((v) << 15) & GENMASK(22, 15)) + +#define G1_REG_APF_THRESHOLD(v) (((v) << 0) & GENMASK(13, 0)) + +static void +hantro_g1_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, + struct hantro_ctx *ctx) +{ + struct v4l2_ctrl_mpeg2_quantisation *q; + + q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); + hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); + vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, G1_REG_QTABLE_BASE); +} + +static void +hantro_g1_mpeg2_dec_set_buffers(struct hantro_dev *vpu, struct hantro_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf, + const struct v4l2_ctrl_mpeg2_sequence *seq, + const struct v4l2_ctrl_mpeg2_picture *pic) +{ + dma_addr_t forward_addr = 0, backward_addr = 0; + dma_addr_t current_addr, addr; + + switch (pic->picture_coding_type) { + case V4L2_MPEG2_PIC_CODING_TYPE_B: + backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); + fallthrough; + case V4L2_MPEG2_PIC_CODING_TYPE_P: + forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); + } + + /* Source bitstream buffer */ + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + vdpu_write_relaxed(vpu, addr, G1_REG_RLC_VLC_BASE); + + /* Destination frame buffer */ + addr = hantro_get_dec_buf_addr(ctx, dst_buf); + current_addr = addr; + + if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) + addr += ALIGN(ctx->dst_fmt.width, 16); + vdpu_write_relaxed(vpu, addr, G1_REG_DEC_OUT_BASE); + + if (!forward_addr) + forward_addr = current_addr; + if (!backward_addr) + backward_addr = current_addr; + + /* Set forward ref frame (top/bottom field) */ + if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || + pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || + (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && + pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST) || + (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && + !(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST))) { + vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); + } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { + vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER1_BASE); + } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { + vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); + } + + /* Set backward ref frame (top/bottom field) */ + vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER2_BASE); + vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER3_BASE); +} + +int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct v4l2_ctrl_mpeg2_sequence *seq; + const struct v4l2_ctrl_mpeg2_picture *pic; + u32 reg; + + src_buf = hantro_get_src_buf(ctx); + dst_buf = hantro_get_dst_buf(ctx); + + /* Apply request controls if any */ + hantro_start_prepare_run(ctx); + + seq = hantro_get_ctrl(ctx, + V4L2_CID_STATELESS_MPEG2_SEQUENCE); + pic = hantro_get_ctrl(ctx, + V4L2_CID_STATELESS_MPEG2_PICTURE); + + reg = G1_REG_DEC_AXI_RD_ID(0) | + G1_REG_DEC_TIMEOUT_E(1) | + G1_REG_DEC_STRSWAP32_E(1) | + G1_REG_DEC_STRENDIAN_E(1) | + G1_REG_DEC_INSWAP32_E(1) | + G1_REG_DEC_OUTSWAP32_E(1) | + G1_REG_DEC_DATA_DISC_E(0) | + G1_REG_DEC_LATENCY(0) | + G1_REG_DEC_CLK_GATE_E(1) | + G1_REG_DEC_IN_ENDIAN(1) | + G1_REG_DEC_OUT_ENDIAN(1) | + G1_REG_DEC_ADV_PRE_DIS(0) | + G1_REG_DEC_SCMD_DIS(0) | + G1_REG_DEC_MAX_BURST(16); + vdpu_write_relaxed(vpu, reg, G1_SWREG(2)); + + reg = G1_REG_DEC_MODE(5) | + G1_REG_RLC_MODE_E(0) | + G1_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | + G1_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | + G1_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | + G1_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | + G1_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | + G1_REG_FWD_INTERLACE_E(0) | + G1_REG_FILTERING_DIS(1) | + G1_REG_WRITE_MVS_E(0) | + G1_REG_DEC_AXI_WR_ID(0); + vdpu_write_relaxed(vpu, reg, G1_SWREG(3)); + + reg = G1_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | + G1_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | + G1_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | + G1_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); + vdpu_write_relaxed(vpu, reg, G1_SWREG(4)); + + reg = G1_REG_STRM_START_BIT(0) | + G1_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | + G1_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | + G1_REG_INTRA_DC_PREC(pic->intra_dc_precision) | + G1_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | + G1_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); + vdpu_write_relaxed(vpu, reg, G1_SWREG(5)); + + reg = G1_REG_INIT_QP(1) | + G1_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); + vdpu_write_relaxed(vpu, reg, G1_SWREG(6)); + + reg = G1_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | + G1_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | + G1_REG_FCODE_FWD_VER(pic->f_code[0][1]) | + G1_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | + G1_REG_FCODE_BWD_VER(pic->f_code[1][1]) | + G1_REG_MV_ACCURACY_FWD(1) | + G1_REG_MV_ACCURACY_BWD(1); + vdpu_write_relaxed(vpu, reg, G1_SWREG(18)); + + reg = G1_REG_STARTMB_X(0) | + G1_REG_STARTMB_Y(0); + vdpu_write_relaxed(vpu, reg, G1_SWREG(48)); + + reg = G1_REG_APF_THRESHOLD(8); + vdpu_write_relaxed(vpu, reg, G1_SWREG(55)); + + hantro_g1_mpeg2_dec_set_quantisation(vpu, ctx); + hantro_g1_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf, + seq, pic); + + hantro_end_prepare_run(ctx); + + vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_g1_regs.h b/drivers/media/platform/verisilicon/hantro_g1_regs.h new file mode 100644 index 000000000000..c623b3b0be18 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g1_regs.h @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright 2018 Google LLC. + * Tomasz Figa + */ + +#ifndef HANTRO_G1_REGS_H_ +#define HANTRO_G1_REGS_H_ + +#define G1_SWREG(nr) ((nr) * 4) + +/* Decoder registers. */ +#define G1_REG_INTERRUPT 0x004 +#define G1_REG_INTERRUPT_DEC_PIC_INF BIT(24) +#define G1_REG_INTERRUPT_DEC_TIMEOUT BIT(18) +#define G1_REG_INTERRUPT_DEC_SLICE_INT BIT(17) +#define G1_REG_INTERRUPT_DEC_ERROR_INT BIT(16) +#define G1_REG_INTERRUPT_DEC_ASO_INT BIT(15) +#define G1_REG_INTERRUPT_DEC_BUFFER_INT BIT(14) +#define G1_REG_INTERRUPT_DEC_BUS_INT BIT(13) +#define G1_REG_INTERRUPT_DEC_RDY_INT BIT(12) +#define G1_REG_INTERRUPT_DEC_IRQ BIT(8) +#define G1_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) +#define G1_REG_INTERRUPT_DEC_E BIT(0) +#define G1_REG_CONFIG 0x008 +#define G1_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 24) +#define G1_REG_CONFIG_DEC_TIMEOUT_E BIT(23) +#define G1_REG_CONFIG_DEC_STRSWAP32_E BIT(22) +#define G1_REG_CONFIG_DEC_STRENDIAN_E BIT(21) +#define G1_REG_CONFIG_DEC_INSWAP32_E BIT(20) +#define G1_REG_CONFIG_DEC_OUTSWAP32_E BIT(19) +#define G1_REG_CONFIG_DEC_DATA_DISC_E BIT(18) +#define G1_REG_CONFIG_TILED_MODE_MSB BIT(17) +#define G1_REG_CONFIG_DEC_OUT_TILED_E BIT(17) +#define G1_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 11) +#define G1_REG_CONFIG_DEC_CLK_GATE_E BIT(10) +#define G1_REG_CONFIG_DEC_IN_ENDIAN BIT(9) +#define G1_REG_CONFIG_DEC_OUT_ENDIAN BIT(8) +#define G1_REG_CONFIG_PRIORITY_MODE(x) (((x) & 0x7) << 5) +#define G1_REG_CONFIG_TILED_MODE_LSB BIT(7) +#define G1_REG_CONFIG_DEC_ADV_PRE_DIS BIT(6) +#define G1_REG_CONFIG_DEC_SCMD_DIS BIT(5) +#define G1_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 0) +#define G1_REG_DEC_CTRL0 0x00c +#define G1_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 28) +#define G1_REG_DEC_CTRL0_RLC_MODE_E BIT(27) +#define G1_REG_DEC_CTRL0_SKIP_MODE BIT(26) +#define G1_REG_DEC_CTRL0_DIVX3_E BIT(25) +#define G1_REG_DEC_CTRL0_PJPEG_E BIT(24) +#define G1_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(23) +#define G1_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(22) +#define G1_REG_DEC_CTRL0_PIC_B_E BIT(21) +#define G1_REG_DEC_CTRL0_PIC_INTER_E BIT(20) +#define G1_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(19) +#define G1_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(18) +#define G1_REG_DEC_CTRL0_SORENSON_E BIT(17) +#define G1_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(16) +#define G1_REG_DEC_CTRL0_DEC_OUT_DIS BIT(15) +#define G1_REG_DEC_CTRL0_FILTERING_DIS BIT(14) +#define G1_REG_DEC_CTRL0_WEBP_E BIT(13) +#define G1_REG_DEC_CTRL0_MVC_E BIT(13) +#define G1_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(13) +#define G1_REG_DEC_CTRL0_WRITE_MVS_E BIT(12) +#define G1_REG_DEC_CTRL0_REFTOPFIRST_E BIT(11) +#define G1_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(10) +#define G1_REG_DEC_CTRL0_PICORD_COUNT_E BIT(9) +#define G1_REG_DEC_CTRL0_DEC_AHB_HLOCK_E BIT(8) +#define G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 0) +/* Setting AXI ID to 0xff to get auto generated ID to avoid possible conflicts */ +#define G1_REG_DEC_CTRL0_DEC_AXI_AUTO G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(0xff) +#define G1_REG_DEC_CTRL1 0x010 +#define G1_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) +#define G1_REG_DEC_CTRL1_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) +#define G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) +#define G1_REG_DEC_CTRL1_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) +#define G1_REG_DEC_CTRL1_ALT_SCAN_E BIT(6) +#define G1_REG_DEC_CTRL1_TOPFIELDFIRST_E BIT(5) +#define G1_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) +#define G1_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) +#define G1_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) +#define G1_REG_DEC_CTRL1_PIC_REFER_FLAG BIT(0) +#define G1_REG_DEC_CTRL2 0x014 +#define G1_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) +#define G1_REG_DEC_CTRL2_SYNC_MARKER_E BIT(25) +#define G1_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(24) +#define G1_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 19) +#define G1_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 14) +#define G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) +#define G1_REG_DEC_CTRL2_INTRADC_VLC_THR(x) (((x) & 0x7) << 16) +#define G1_REG_DEC_CTRL2_VOP_TIME_INCR(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL2_DQ_PROFILE BIT(24) +#define G1_REG_DEC_CTRL2_DQBI_LEVEL BIT(23) +#define G1_REG_DEC_CTRL2_RANGE_RED_FRM_E BIT(22) +#define G1_REG_DEC_CTRL2_FAST_UVMC_E BIT(20) +#define G1_REG_DEC_CTRL2_TRANSDCTAB BIT(17) +#define G1_REG_DEC_CTRL2_TRANSACFRM(x) (((x) & 0x3) << 15) +#define G1_REG_DEC_CTRL2_TRANSACFRM2(x) (((x) & 0x3) << 13) +#define G1_REG_DEC_CTRL2_MB_MODE_TAB(x) (((x) & 0x7) << 10) +#define G1_REG_DEC_CTRL2_MVTAB(x) (((x) & 0x7) << 7) +#define G1_REG_DEC_CTRL2_CBPTAB(x) (((x) & 0x7) << 4) +#define G1_REG_DEC_CTRL2_2MV_BLK_PAT_TAB(x) (((x) & 0x3) << 2) +#define G1_REG_DEC_CTRL2_4MV_BLK_PAT_TAB(x) (((x) & 0x3) << 0) +#define G1_REG_DEC_CTRL2_QSCALE_TYPE BIT(24) +#define G1_REG_DEC_CTRL2_CON_MV_E BIT(4) +#define G1_REG_DEC_CTRL2_INTRA_DC_PREC(x) (((x) & 0x3) << 2) +#define G1_REG_DEC_CTRL2_INTRA_VLC_TAB BIT(1) +#define G1_REG_DEC_CTRL2_FRAME_PRED_DCT BIT(0) +#define G1_REG_DEC_CTRL2_JPEG_QTABLES(x) (((x) & 0x3) << 11) +#define G1_REG_DEC_CTRL2_JPEG_MODE(x) (((x) & 0x7) << 8) +#define G1_REG_DEC_CTRL2_JPEG_FILRIGHT_E BIT(7) +#define G1_REG_DEC_CTRL2_JPEG_STREAM_ALL BIT(6) +#define G1_REG_DEC_CTRL2_CR_AC_VLCTABLE BIT(5) +#define G1_REG_DEC_CTRL2_CB_AC_VLCTABLE BIT(4) +#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE BIT(3) +#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE BIT(2) +#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE3 BIT(1) +#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE3 BIT(0) +#define G1_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) +#define G1_REG_DEC_CTRL2_HUFFMAN_E BIT(17) +#define G1_REG_DEC_CTRL2_MULTISTREAM_E BIT(16) +#define G1_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) +#define G1_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) +#define G1_REG_DEC_CTRL2_ALPHA_OFFSET(x) (((x) & 0x1f) << 5) +#define G1_REG_DEC_CTRL2_BETA_OFFSET(x) (((x) & 0x1f) << 0) +#define G1_REG_DEC_CTRL3 0x018 +#define G1_REG_DEC_CTRL3_START_CODE_E BIT(31) +#define G1_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) +#define G1_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(24) +#define G1_REG_DEC_CTRL3_STREAM_LEN_EXT(x) (((x) & 0xff) << 24) +#define G1_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) +#define G1_REG_DEC_CTRL4 0x01c +#define G1_REG_DEC_CTRL4_CABAC_E BIT(31) +#define G1_REG_DEC_CTRL4_BLACKWHITE_E BIT(30) +#define G1_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(29) +#define G1_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(28) +#define G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 26) +#define G1_REG_DEC_CTRL4_AVS_H264_H_EXT BIT(25) +#define G1_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) +#define G1_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL4_BITPLANE0_E BIT(31) +#define G1_REG_DEC_CTRL4_BITPLANE1_E BIT(30) +#define G1_REG_DEC_CTRL4_BITPLANE2_E BIT(29) +#define G1_REG_DEC_CTRL4_ALT_PQUANT(x) (((x) & 0x1f) << 24) +#define G1_REG_DEC_CTRL4_DQ_EDGES(x) (((x) & 0xf) << 20) +#define G1_REG_DEC_CTRL4_TTMBF BIT(19) +#define G1_REG_DEC_CTRL4_PQINDEX(x) (((x) & 0x1f) << 14) +#define G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) +#define G1_REG_DEC_CTRL4_BILIN_MC_E BIT(12) +#define G1_REG_DEC_CTRL4_UNIQP_E BIT(11) +#define G1_REG_DEC_CTRL4_HALFQP_E BIT(10) +#define G1_REG_DEC_CTRL4_TTFRM(x) (((x) & 0x3) << 8) +#define G1_REG_DEC_CTRL4_2ND_BYTE_EMUL_E BIT(7) +#define G1_REG_DEC_CTRL4_DQUANT_E BIT(6) +#define G1_REG_DEC_CTRL4_VC1_ADV_E BIT(5) +#define G1_REG_DEC_CTRL4_PJPEG_FILDOWN_E BIT(26) +#define G1_REG_DEC_CTRL4_PJPEG_WDIV8 BIT(25) +#define G1_REG_DEC_CTRL4_PJPEG_HDIV8 BIT(24) +#define G1_REG_DEC_CTRL4_PJPEG_AH(x) (((x) & 0xf) << 20) +#define G1_REG_DEC_CTRL4_PJPEG_AL(x) (((x) & 0xf) << 16) +#define G1_REG_DEC_CTRL4_PJPEG_SS(x) (((x) & 0xff) << 8) +#define G1_REG_DEC_CTRL4_PJPEG_SE(x) (((x) & 0xff) << 0) +#define G1_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) +#define G1_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) +#define G1_REG_DEC_CTRL4_CH_MV_RES BIT(13) +#define G1_REG_DEC_CTRL4_INIT_DC_MATCH0(x) (((x) & 0x7) << 9) +#define G1_REG_DEC_CTRL4_INIT_DC_MATCH1(x) (((x) & 0x7) << 6) +#define G1_REG_DEC_CTRL4_VP7_VERSION BIT(5) +#define G1_REG_DEC_CTRL5 0x020 +#define G1_REG_DEC_CTRL5_CONST_INTRA_E BIT(31) +#define G1_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(30) +#define G1_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(29) +#define G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(28) +#define G1_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 17) +#define G1_REG_DEC_CTRL5_IDR_PIC_E BIT(16) +#define G1_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL5_MV_SCALEFACTOR(x) (((x) & 0xff) << 24) +#define G1_REG_DEC_CTRL5_REF_DIST_FWD(x) (((x) & 0x1f) << 19) +#define G1_REG_DEC_CTRL5_REF_DIST_BWD(x) (((x) & 0x1f) << 14) +#define G1_REG_DEC_CTRL5_LOOP_FILT_LIMIT(x) (((x) & 0xf) << 14) +#define G1_REG_DEC_CTRL5_VARIANCE_TEST_E BIT(13) +#define G1_REG_DEC_CTRL5_MV_THRESHOLD(x) (((x) & 0x7) << 10) +#define G1_REG_DEC_CTRL5_VAR_THRESHOLD(x) (((x) & 0x3ff) << 0) +#define G1_REG_DEC_CTRL5_DIVX_IDCT_E BIT(8) +#define G1_REG_DEC_CTRL5_DIVX3_SLICE_SIZE(x) (((x) & 0xff) << 0) +#define G1_REG_DEC_CTRL5_PJPEG_REST_FREQ(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL5_RV_PROFILE(x) (((x) & 0x3) << 30) +#define G1_REG_DEC_CTRL5_RV_OSV_QUANT(x) (((x) & 0x3) << 28) +#define G1_REG_DEC_CTRL5_RV_FWD_SCALE(x) (((x) & 0x3fff) << 14) +#define G1_REG_DEC_CTRL5_RV_BWD_SCALE(x) (((x) & 0x3fff) << 0) +#define G1_REG_DEC_CTRL5_INIT_DC_COMP0(x) (((x) & 0xffff) << 16) +#define G1_REG_DEC_CTRL5_INIT_DC_COMP1(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL6 0x024 +#define G1_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) +#define G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) +#define G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) +#define G1_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) +#define G1_REG_DEC_CTRL6_ICOMP0_E BIT(24) +#define G1_REG_DEC_CTRL6_ISCALE0(x) (((x) & 0xff) << 16) +#define G1_REG_DEC_CTRL6_ISHIFT0(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) +#define G1_REG_DEC_CTRL6_PIC_SLICE_AM(x) (((x) & 0x1fff) << 0) +#define G1_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) +#define G1_REG_FWD_PIC(i) (0x028 + ((i) * 0x4)) +#define G1_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) +#define G1_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) +#define G1_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) +#define G1_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) +#define G1_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) +#define G1_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) +#define G1_REG_FWD_PIC1_ICOMP1_E BIT(24) +#define G1_REG_FWD_PIC1_ISCALE1(x) (((x) & 0xff) << 16) +#define G1_REG_FWD_PIC1_ISHIFT1(x) (((x) & 0xffff) << 0) +#define G1_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) +#define G1_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) +#define G1_REG_FWD_PIC1_SEGMENT_E BIT(0) +#define G1_REG_DEC_CTRL7 0x02c +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F15(x) (((x) & 0x1f) << 25) +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F14(x) (((x) & 0x1f) << 20) +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F13(x) (((x) & 0x1f) << 15) +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F12(x) (((x) & 0x1f) << 10) +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F11(x) (((x) & 0x1f) << 5) +#define G1_REG_DEC_CTRL7_PINIT_RLIST_F10(x) (((x) & 0x1f) << 0) +#define G1_REG_DEC_CTRL7_ICOMP2_E BIT(24) +#define G1_REG_DEC_CTRL7_ISCALE2(x) (((x) & 0xff) << 16) +#define G1_REG_DEC_CTRL7_ISHIFT2(x) (((x) & 0xffff) << 0) +#define G1_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) +#define G1_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) +#define G1_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) +#define G1_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) +#define G1_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) +#define G1_REG_ADDR_STR 0x030 +#define G1_REG_ADDR_DST 0x034 +#define G1_REG_ADDR_REF(i) (0x038 + ((i) * 0x4)) +#define G1_REG_ADDR_REF_FIELD_E BIT(1) +#define G1_REG_ADDR_REF_TOPC_E BIT(0) +#define G1_REG_REF_PIC(i) (0x078 + ((i) * 0x4)) +#define G1_REG_REF_PIC_FILT_TYPE_E BIT(31) +#define G1_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) +#define G1_REG_REF_PIC_MB_ADJ_0(x) (((x) & 0x7f) << 21) +#define G1_REG_REF_PIC_MB_ADJ_1(x) (((x) & 0x7f) << 14) +#define G1_REG_REF_PIC_MB_ADJ_2(x) (((x) & 0x7f) << 7) +#define G1_REG_REF_PIC_MB_ADJ_3(x) (((x) & 0x7f) << 0) +#define G1_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) +#define G1_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) +#define G1_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) +#define G1_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) +#define G1_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) +#define G1_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) +#define G1_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) +#define G1_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) +#define G1_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) +#define G1_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) +#define G1_REG_LT_REF 0x098 +#define G1_REG_VALID_REF 0x09c +#define G1_REG_ADDR_QTABLE 0x0a0 +#define G1_REG_ADDR_DIR_MV 0x0a4 +#define G1_REG_BD_REF_PIC(i) (0x0a8 + ((i) * 0x4)) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 25) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 20) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 15) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 10) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 5) +#define G1_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) +#define G1_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) +#define G1_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) +#define G1_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) +#define G1_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) +#define G1_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) +#define G1_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) +#define G1_REG_BD_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) +#define G1_REG_BD_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) +#define G1_REG_BD_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) +#define G1_REG_BD_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) +#define G1_REG_BD_P_REF_PIC 0x0bc +#define G1_REG_BD_P_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) +#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 25) +#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 20) +#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 15) +#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 10) +#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 5) +#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 0) +#define G1_REG_ERR_CONC 0x0c0 +#define G1_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 23) +#define G1_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 15) +#define G1_REG_PRED_FLT 0x0c4 +#define G1_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) +#define G1_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) +#define G1_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) +#define G1_REG_REF_BUF_CTRL 0x0cc +#define G1_REG_REF_BUF_CTRL_REFBU_E BIT(31) +#define G1_REG_REF_BUF_CTRL_REFBU_THR(x) (((x) & 0xfff) << 19) +#define G1_REG_REF_BUF_CTRL_REFBU_PICID(x) (((x) & 0x1f) << 14) +#define G1_REG_REF_BUF_CTRL_REFBU_EVAL_E BIT(13) +#define G1_REG_REF_BUF_CTRL_REFBU_FPARMOD_E BIT(12) +#define G1_REG_REF_BUF_CTRL_REFBU_Y_OFFSET(x) (((x) & 0x1ff) << 0) +#define G1_REG_REF_BUF_CTRL2 0x0dc +#define G1_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(31) +#define G1_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 19) +#define G1_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 14) +#define G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 0) +#define G1_REG_SOFT_RESET 0x194 + +/* Post-processor registers. */ +#define G1_REG_PP_INTERRUPT G1_SWREG(60) +#define G1_REG_PP_READY_IRQ BIT(12) +#define G1_REG_PP_IRQ BIT(8) +#define G1_REG_PP_IRQ_DIS BIT(4) +#define G1_REG_PP_PIPELINE_EN BIT(1) +#define G1_REG_PP_EXTERNAL_TRIGGER BIT(0) +#define G1_REG_PP_DEV_CONFIG G1_SWREG(61) +#define G1_REG_PP_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) +#define G1_REG_PP_AXI_WR_ID(v) (((v) << 16) & GENMASK(23, 16)) +#define G1_REG_PP_INSWAP32_E(v) ((v) ? BIT(10) : 0) +#define G1_REG_PP_DATA_DISC_E(v) ((v) ? BIT(9) : 0) +#define G1_REG_PP_CLK_GATE_E(v) ((v) ? BIT(8) : 0) +#define G1_REG_PP_IN_ENDIAN(v) ((v) ? BIT(7) : 0) +#define G1_REG_PP_OUT_ENDIAN(v) ((v) ? BIT(6) : 0) +#define G1_REG_PP_OUTSWAP32_E(v) ((v) ? BIT(5) : 0) +#define G1_REG_PP_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) +#define G1_REG_PP_IN_LUMA_BASE G1_SWREG(63) +#define G1_REG_PP_IN_CB_BASE G1_SWREG(64) +#define G1_REG_PP_IN_CR_BASE G1_SWREG(65) +#define G1_REG_PP_OUT_LUMA_BASE G1_SWREG(66) +#define G1_REG_PP_OUT_CHROMA_BASE G1_SWREG(67) +#define G1_REG_PP_CONTRAST_ADJUST G1_SWREG(68) +#define G1_REG_PP_COLOR_CONVERSION G1_SWREG(69) +#define G1_REG_PP_COLOR_CONVERSION0 G1_SWREG(70) +#define G1_REG_PP_COLOR_CONVERSION1 G1_SWREG(71) +#define G1_REG_PP_INPUT_SIZE G1_SWREG(72) +#define G1_REG_PP_INPUT_SIZE_HEIGHT(v) (((v) << 9) & GENMASK(16, 9)) +#define G1_REG_PP_INPUT_SIZE_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) +#define G1_REG_PP_SCALING0 G1_SWREG(79) +#define G1_REG_PP_PADD_R(v) (((v) << 23) & GENMASK(27, 23)) +#define G1_REG_PP_PADD_G(v) (((v) << 18) & GENMASK(22, 18)) +#define G1_REG_PP_RANGEMAP_Y(v) ((v) ? BIT(31) : 0) +#define G1_REG_PP_RANGEMAP_C(v) ((v) ? BIT(30) : 0) +#define G1_REG_PP_YCBCR_RANGE(v) ((v) ? BIT(29) : 0) +#define G1_REG_PP_RGB_16(v) ((v) ? BIT(28) : 0) +#define G1_REG_PP_SCALING1 G1_SWREG(80) +#define G1_REG_PP_PADD_B(v) (((v) << 18) & GENMASK(22, 18)) +#define G1_REG_PP_MASK_R G1_SWREG(82) +#define G1_REG_PP_MASK_G G1_SWREG(83) +#define G1_REG_PP_MASK_B G1_SWREG(84) +#define G1_REG_PP_CONTROL G1_SWREG(85) +#define G1_REG_PP_CONTROL_IN_FMT(v) (((v) << 29) & GENMASK(31, 29)) +#define G1_REG_PP_CONTROL_OUT_FMT(v) (((v) << 26) & GENMASK(28, 26)) +#define G1_REG_PP_CONTROL_OUT_HEIGHT(v) (((v) << 15) & GENMASK(25, 15)) +#define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4)) +#define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88) +#define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) +#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92) +#define G1_REG_PP_FUSE G1_SWREG(99) + +#endif /* HANTRO_G1_REGS_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c b/drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c new file mode 100644 index 000000000000..851eb67f19f5 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VP8 codec driver + * + * Copyright (C) 2019 Rockchip Electronics Co., Ltd. + * ZhiChao Yu + * + * Copyright (C) 2019 Google, Inc. + * Tomasz Figa + */ + +#include + +#include "hantro_hw.h" +#include "hantro.h" +#include "hantro_g1_regs.h" + +/* DCT partition base address regs */ +static const struct hantro_reg vp8_dec_dct_base[8] = { + { G1_REG_ADDR_STR, 0, 0xffffffff }, + { G1_REG_ADDR_REF(8), 0, 0xffffffff }, + { G1_REG_ADDR_REF(9), 0, 0xffffffff }, + { G1_REG_ADDR_REF(10), 0, 0xffffffff }, + { G1_REG_ADDR_REF(11), 0, 0xffffffff }, + { G1_REG_ADDR_REF(12), 0, 0xffffffff }, + { G1_REG_ADDR_REF(14), 0, 0xffffffff }, + { G1_REG_ADDR_REF(15), 0, 0xffffffff }, +}; + +/* Loop filter level regs */ +static const struct hantro_reg vp8_dec_lf_level[4] = { + { G1_REG_REF_PIC(2), 18, 0x3f }, + { G1_REG_REF_PIC(2), 12, 0x3f }, + { G1_REG_REF_PIC(2), 6, 0x3f }, + { G1_REG_REF_PIC(2), 0, 0x3f }, +}; + +/* Macroblock loop filter level adjustment regs */ +static const struct hantro_reg vp8_dec_mb_adj[4] = { + { G1_REG_REF_PIC(0), 21, 0x7f }, + { G1_REG_REF_PIC(0), 14, 0x7f }, + { G1_REG_REF_PIC(0), 7, 0x7f }, + { G1_REG_REF_PIC(0), 0, 0x7f }, +}; + +/* Reference frame adjustment regs */ +static const struct hantro_reg vp8_dec_ref_adj[4] = { + { G1_REG_REF_PIC(1), 21, 0x7f }, + { G1_REG_REF_PIC(1), 14, 0x7f }, + { G1_REG_REF_PIC(1), 7, 0x7f }, + { G1_REG_REF_PIC(1), 0, 0x7f }, +}; + +/* Quantizer */ +static const struct hantro_reg vp8_dec_quant[4] = { + { G1_REG_REF_PIC(3), 11, 0x7ff }, + { G1_REG_REF_PIC(3), 0, 0x7ff }, + { G1_REG_BD_REF_PIC(4), 11, 0x7ff }, + { G1_REG_BD_REF_PIC(4), 0, 0x7ff }, +}; + +/* Quantizer delta regs */ +static const struct hantro_reg vp8_dec_quant_delta[5] = { + { G1_REG_REF_PIC(3), 27, 0x1f }, + { G1_REG_REF_PIC(3), 22, 0x1f }, + { G1_REG_BD_REF_PIC(4), 27, 0x1f }, + { G1_REG_BD_REF_PIC(4), 22, 0x1f }, + { G1_REG_BD_P_REF_PIC, 27, 0x1f }, +}; + +/* DCT partition start bits regs */ +static const struct hantro_reg vp8_dec_dct_start_bits[8] = { + { G1_REG_DEC_CTRL2, 26, 0x3f }, { G1_REG_DEC_CTRL4, 26, 0x3f }, + { G1_REG_DEC_CTRL4, 20, 0x3f }, { G1_REG_DEC_CTRL7, 24, 0x3f }, + { G1_REG_DEC_CTRL7, 18, 0x3f }, { G1_REG_DEC_CTRL7, 12, 0x3f }, + { G1_REG_DEC_CTRL7, 6, 0x3f }, { G1_REG_DEC_CTRL7, 0, 0x3f }, +}; + +/* Precision filter tap regs */ +static const struct hantro_reg vp8_dec_pred_bc_tap[8][4] = { + { + { G1_REG_PRED_FLT, 22, 0x3ff }, + { G1_REG_PRED_FLT, 12, 0x3ff }, + { G1_REG_PRED_FLT, 2, 0x3ff }, + { G1_REG_REF_PIC(4), 22, 0x3ff }, + }, + { + { G1_REG_REF_PIC(4), 12, 0x3ff }, + { G1_REG_REF_PIC(4), 2, 0x3ff }, + { G1_REG_REF_PIC(5), 22, 0x3ff }, + { G1_REG_REF_PIC(5), 12, 0x3ff }, + }, + { + { G1_REG_REF_PIC(5), 2, 0x3ff }, + { G1_REG_REF_PIC(6), 22, 0x3ff }, + { G1_REG_REF_PIC(6), 12, 0x3ff }, + { G1_REG_REF_PIC(6), 2, 0x3ff }, + }, + { + { G1_REG_REF_PIC(7), 22, 0x3ff }, + { G1_REG_REF_PIC(7), 12, 0x3ff }, + { G1_REG_REF_PIC(7), 2, 0x3ff }, + { G1_REG_LT_REF, 22, 0x3ff }, + }, + { + { G1_REG_LT_REF, 12, 0x3ff }, + { G1_REG_LT_REF, 2, 0x3ff }, + { G1_REG_VALID_REF, 22, 0x3ff }, + { G1_REG_VALID_REF, 12, 0x3ff }, + }, + { + { G1_REG_VALID_REF, 2, 0x3ff }, + { G1_REG_BD_REF_PIC(0), 22, 0x3ff }, + { G1_REG_BD_REF_PIC(0), 12, 0x3ff }, + { G1_REG_BD_REF_PIC(0), 2, 0x3ff }, + }, + { + { G1_REG_BD_REF_PIC(1), 22, 0x3ff }, + { G1_REG_BD_REF_PIC(1), 12, 0x3ff }, + { G1_REG_BD_REF_PIC(1), 2, 0x3ff }, + { G1_REG_BD_REF_PIC(2), 22, 0x3ff }, + }, + { + { G1_REG_BD_REF_PIC(2), 12, 0x3ff }, + { G1_REG_BD_REF_PIC(2), 2, 0x3ff }, + { G1_REG_BD_REF_PIC(3), 22, 0x3ff }, + { G1_REG_BD_REF_PIC(3), 12, 0x3ff }, + }, +}; + +/* + * Set loop filters + */ +static void cfg_lf(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + const struct v4l2_vp8_segment *seg = &hdr->segment; + const struct v4l2_vp8_loop_filter *lf = &hdr->lf; + struct hantro_dev *vpu = ctx->dev; + unsigned int i; + u32 reg; + + if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { + hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); + } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { + for (i = 0; i < 4; i++) { + u32 lf_level = clamp(lf->level + seg->lf_update[i], + 0, 63); + + hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); + } + } else { + for (i = 0; i < 4; i++) + hantro_reg_write(vpu, &vp8_dec_lf_level[i], + seg->lf_update[i]); + } + + reg = G1_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); + if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) + reg |= G1_REG_REF_PIC_FILT_TYPE_E; + vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(0)); + + if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { + for (i = 0; i < 4; i++) { + hantro_reg_write(vpu, &vp8_dec_mb_adj[i], + lf->mb_mode_delta[i]); + hantro_reg_write(vpu, &vp8_dec_ref_adj[i], + lf->ref_frm_delta[i]); + } + } +} + +/* + * Set quantization parameters + */ +static void cfg_qp(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + const struct v4l2_vp8_quantization *q = &hdr->quant; + const struct v4l2_vp8_segment *seg = &hdr->segment; + struct hantro_dev *vpu = ctx->dev; + unsigned int i; + + if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { + hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); + } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { + for (i = 0; i < 4; i++) { + u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], + 0, 127); + + hantro_reg_write(vpu, &vp8_dec_quant[i], quant); + } + } else { + for (i = 0; i < 4; i++) + hantro_reg_write(vpu, &vp8_dec_quant[i], + seg->quant_update[i]); + } + + hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); +} + +/* + * set control partition and DCT partition regs + * + * VP8 frame stream data layout: + * + * first_part_size parttion_sizes[0] + * ^ ^ + * src_dma | | + * ^ +--------+------+ +-----+-----+ + * | | control part | | | + * +--------+----------------+------------------+-----------+-----+-----------+ + * | tag 3B | extra 7B | hdr | mb_data | DCT sz | DCT part0 | ... | DCT partn | + * +--------+-----------------------------------+-----------+-----+-----------+ + * | | | | + * v +----+---+ v + * mb_start | src_dma_end + * v + * DCT size part + * (num_dct-1)*3B + * Note: + * 1. only key-frames have extra 7-bytes + * 2. all offsets are base on src_dma + * 3. number of DCT parts is 1, 2, 4 or 8 + * 4. the addresses set to the VPU must be 64-bits aligned + */ +static void cfg_parts(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *vb2_src; + u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; + u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; + u32 dct_size_part_size, dct_part_offset; + struct hantro_reg reg; + dma_addr_t src_dma; + u32 dct_part_total_len = 0; + u32 count = 0; + unsigned int i; + + vb2_src = hantro_get_src_buf(ctx); + src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); + + /* + * Calculate control partition mb data info + * @first_part_header_bits: bits offset of mb data from first + * part start pos + * @mb_offset_bits: bits offset of mb data from src_dma + * base addr + * @mb_offset_byte: bytes offset of mb data from src_dma + * base addr + * @mb_start_bits: bits offset of mb data from mb data + * 64bits alignment addr + */ + mb_offset_bits = first_part_offset * 8 + + hdr->first_part_header_bits + 8; + mb_offset_bytes = mb_offset_bits / 8; + mb_start_bits = mb_offset_bits - + (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; + mb_size = hdr->first_part_size - + (mb_offset_bytes - first_part_offset) + + (mb_offset_bytes & DEC_8190_ALIGN_MASK); + + /* Macroblock data aligned base addr */ + vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) + + src_dma, G1_REG_ADDR_REF(13)); + + /* Macroblock data start bits */ + reg.base = G1_REG_DEC_CTRL2; + reg.mask = 0x3f; + reg.shift = 18; + hantro_reg_write(vpu, ®, mb_start_bits); + + /* Macroblock aligned data length */ + reg.base = G1_REG_DEC_CTRL6; + reg.mask = 0x3fffff; + reg.shift = 0; + hantro_reg_write(vpu, ®, mb_size + 1); + + /* + * Calculate DCT partition info + * @dct_size_part_size: Containing sizes of DCT part, every DCT part + * has 3 bytes to store its size, except the last + * DCT part + * @dct_part_offset: bytes offset of DCT parts from src_dma base addr + * @dct_part_total_len: total size of all DCT parts + */ + dct_size_part_size = (hdr->num_dct_parts - 1) * 3; + dct_part_offset = first_part_offset + hdr->first_part_size; + for (i = 0; i < hdr->num_dct_parts; i++) + dct_part_total_len += hdr->dct_part_sizes[i]; + dct_part_total_len += dct_size_part_size; + dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); + + /* Number of DCT partitions */ + reg.base = G1_REG_DEC_CTRL6; + reg.mask = 0xf; + reg.shift = 24; + hantro_reg_write(vpu, ®, hdr->num_dct_parts - 1); + + /* DCT partition length */ + vdpu_write_relaxed(vpu, + G1_REG_DEC_CTRL3_STREAM_LEN(dct_part_total_len), + G1_REG_DEC_CTRL3); + + /* DCT partitions base address */ + for (i = 0; i < hdr->num_dct_parts; i++) { + u32 byte_offset = dct_part_offset + dct_size_part_size + count; + u32 base_addr = byte_offset + src_dma; + + hantro_reg_write(vpu, &vp8_dec_dct_base[i], + base_addr & (~DEC_8190_ALIGN_MASK)); + + hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], + (byte_offset & DEC_8190_ALIGN_MASK) * 8); + + count += hdr->dct_part_sizes[i]; + } +} + +/* + * prediction filter taps + * normal 6-tap filters + */ +static void cfg_tap(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_reg reg; + u32 val = 0; + int i, j; + + reg.base = G1_REG_BD_REF_PIC(3); + reg.mask = 0xf; + + if ((hdr->version & 0x03) != 0) + return; /* Tap filter not used. */ + + for (i = 0; i < 8; i++) { + val = (hantro_vp8_dec_mc_filter[i][0] << 2) | + hantro_vp8_dec_mc_filter[i][5]; + + for (j = 0; j < 4; j++) + hantro_reg_write(vpu, &vp8_dec_pred_bc_tap[i][j], + hantro_vp8_dec_mc_filter[i][j + 1]); + + switch (i) { + case 2: + reg.shift = 8; + break; + case 4: + reg.shift = 4; + break; + case 6: + reg.shift = 0; + break; + default: + continue; + } + + hantro_reg_write(vpu, ®, val); + } +} + +static void cfg_ref(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr, + struct vb2_v4l2_buffer *vb2_dst) +{ + struct hantro_dev *vpu = ctx->dev; + dma_addr_t ref; + + + ref = hantro_get_ref(ctx, hdr->last_frame_ts); + if (!ref) { + vpu_debug(0, "failed to find last frame ts=%llu\n", + hdr->last_frame_ts); + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + } + vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(0)); + + ref = hantro_get_ref(ctx, hdr->golden_frame_ts); + if (!ref && hdr->golden_frame_ts) + vpu_debug(0, "failed to find golden frame ts=%llu\n", + hdr->golden_frame_ts); + if (!ref) + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) + ref |= G1_REG_ADDR_REF_TOPC_E; + vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(4)); + + ref = hantro_get_ref(ctx, hdr->alt_frame_ts); + if (!ref && hdr->alt_frame_ts) + vpu_debug(0, "failed to find alt frame ts=%llu\n", + hdr->alt_frame_ts); + if (!ref) + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) + ref |= G1_REG_ADDR_REF_TOPC_E; + vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(5)); +} + +static void cfg_buffers(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr, + struct vb2_v4l2_buffer *vb2_dst) +{ + const struct v4l2_vp8_segment *seg = &hdr->segment; + struct hantro_dev *vpu = ctx->dev; + dma_addr_t dst_dma; + u32 reg; + + /* Set probability table buffer address */ + vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, + G1_REG_ADDR_QTABLE); + + /* Set segment map address */ + reg = G1_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); + if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { + reg |= G1_REG_FWD_PIC1_SEGMENT_E; + if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) + reg |= G1_REG_FWD_PIC1_SEGMENT_UPD_E; + } + vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(0)); + + dst_dma = hantro_get_dec_buf_addr(ctx, &vb2_dst->vb2_buf); + vdpu_write_relaxed(vpu, dst_dma, G1_REG_ADDR_DST); +} + +int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx) +{ + const struct v4l2_ctrl_vp8_frame *hdr; + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *vb2_dst; + size_t height = ctx->dst_fmt.height; + size_t width = ctx->dst_fmt.width; + u32 mb_width, mb_height; + u32 reg; + + hantro_start_prepare_run(ctx); + + hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); + if (WARN_ON(!hdr)) + return -EINVAL; + + /* Reset segment_map buffer in keyframe */ + if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) + memset(ctx->vp8_dec.segment_map.cpu, 0, + ctx->vp8_dec.segment_map.size); + + hantro_vp8_prob_update(ctx, hdr); + + reg = G1_REG_CONFIG_DEC_TIMEOUT_E | + G1_REG_CONFIG_DEC_STRENDIAN_E | + G1_REG_CONFIG_DEC_INSWAP32_E | + G1_REG_CONFIG_DEC_STRSWAP32_E | + G1_REG_CONFIG_DEC_OUTSWAP32_E | + G1_REG_CONFIG_DEC_CLK_GATE_E | + G1_REG_CONFIG_DEC_IN_ENDIAN | + G1_REG_CONFIG_DEC_OUT_ENDIAN | + G1_REG_CONFIG_DEC_MAX_BURST(16); + vdpu_write_relaxed(vpu, reg, G1_REG_CONFIG); + + reg = G1_REG_DEC_CTRL0_DEC_MODE(10) | + G1_REG_DEC_CTRL0_DEC_AXI_AUTO; + if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) + reg |= G1_REG_DEC_CTRL0_PIC_INTER_E; + if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) + reg |= G1_REG_DEC_CTRL0_SKIP_MODE; + if (hdr->lf.level == 0) + reg |= G1_REG_DEC_CTRL0_FILTERING_DIS; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); + + /* Frame dimensions */ + mb_width = MB_WIDTH(width); + mb_height = MB_HEIGHT(height); + reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(mb_width) | + G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(mb_height) | + G1_REG_DEC_CTRL1_PIC_MB_W_EXT(mb_width >> 9) | + G1_REG_DEC_CTRL1_PIC_MB_H_EXT(mb_height >> 8); + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); + + /* Boolean decoder */ + reg = G1_REG_DEC_CTRL2_BOOLEAN_RANGE(hdr->coder_state.range) + | G1_REG_DEC_CTRL2_BOOLEAN_VALUE(hdr->coder_state.value); + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); + + reg = 0; + if (hdr->version != 3) + reg |= G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT; + if (hdr->version & 0x3) + reg |= G1_REG_DEC_CTRL4_BILIN_MC_E; + vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); + + cfg_lf(ctx, hdr); + cfg_qp(ctx, hdr); + cfg_parts(ctx, hdr); + cfg_tap(ctx, hdr); + + vb2_dst = hantro_get_dst_buf(ctx); + cfg_ref(ctx, hdr, vb2_dst); + cfg_buffers(ctx, hdr, vb2_dst); + + hantro_end_prepare_run(ctx); + + vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c new file mode 100644 index 000000000000..ee5f14c5f8f2 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2021 Collabora Ltd, Andrzej Pietrasiewicz + */ + +#include "hantro_hw.h" +#include "hantro_g2_regs.h" + +void hantro_g2_check_idle(struct hantro_dev *vpu) +{ + int i; + + for (i = 0; i < 3; i++) { + u32 status; + + /* Make sure the VPU is idle */ + status = vdpu_read(vpu, G2_REG_INTERRUPT); + if (status & G2_REG_INTERRUPT_DEC_E) { + dev_warn(vpu->dev, "device still running, aborting"); + status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS; + vdpu_write(vpu, status, G2_REG_INTERRUPT); + } + } +} + +irqreturn_t hantro_g2_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, G2_REG_INTERRUPT); + state = (status & G2_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, G2_REG_INTERRUPT); + vdpu_write(vpu, G2_REG_CONFIG_DEC_CLK_GATE_E, G2_REG_CONFIG); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c new file mode 100644 index 000000000000..233ecd863d5f --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU HEVC codec driver + * + * Copyright (C) 2020 Safran Passenger Innovations LLC + */ + +#include "hantro_hw.h" +#include "hantro_g2_regs.h" + +#define G2_ALIGN 16 + +static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx) +{ + return ctx->dst_fmt.width * ctx->dst_fmt.height; +} + +static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx) +{ + size_t cr_offset = hantro_hevc_chroma_offset(ctx); + + return ALIGN((cr_offset * 3) / 2, G2_ALIGN); +} + +static void prepare_tile_info_buffer(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu); + unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1; + unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; + unsigned int pic_width_in_ctbs, pic_height_in_ctbs; + unsigned int max_log2_ctb_size, ctb_size; + bool tiles_enabled, uniform_spacing; + u32 no_chroma = 0; + + tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); + uniform_spacing = !!(pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING); + + hantro_reg_write(vpu, &g2_tile_e, tiles_enabled); + + max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 + + sps->log2_diff_max_min_luma_coding_block_size; + pic_width_in_ctbs = (sps->pic_width_in_luma_samples + + (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size; + pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1) + >> max_log2_ctb_size; + ctb_size = 1 << max_log2_ctb_size; + + vpu_debug(1, "Preparing tile sizes buffer for %dx%d CTBs (CTB size %d)\n", + pic_width_in_ctbs, pic_height_in_ctbs, ctb_size); + + if (tiles_enabled) { + unsigned int i, j, h; + + vpu_debug(1, "Tiles enabled! %dx%d\n", num_tile_cols, num_tile_rows); + + hantro_reg_write(vpu, &g2_num_tile_rows, num_tile_rows); + hantro_reg_write(vpu, &g2_num_tile_cols, num_tile_cols); + + /* write width + height for each tile in pic */ + if (!uniform_spacing) { + u32 tmp_w = 0, tmp_h = 0; + + for (i = 0; i < num_tile_rows; i++) { + if (i == num_tile_rows - 1) + h = pic_height_in_ctbs - tmp_h; + else + h = pps->row_height_minus1[i] + 1; + tmp_h += h; + if (i == 0 && h == 1 && ctb_size == 16) + no_chroma = 1; + for (j = 0, tmp_w = 0; j < num_tile_cols - 1; j++) { + tmp_w += pps->column_width_minus1[j] + 1; + *p++ = pps->column_width_minus1[j] + 1; + *p++ = h; + if (i == 0 && h == 1 && ctb_size == 16) + no_chroma = 1; + } + /* last column */ + *p++ = pic_width_in_ctbs - tmp_w; + *p++ = h; + } + } else { /* uniform spacing */ + u32 tmp, prev_h, prev_w; + + for (i = 0, prev_h = 0; i < num_tile_rows; i++) { + tmp = (i + 1) * pic_height_in_ctbs / num_tile_rows; + h = tmp - prev_h; + prev_h = tmp; + if (i == 0 && h == 1 && ctb_size == 16) + no_chroma = 1; + for (j = 0, prev_w = 0; j < num_tile_cols; j++) { + tmp = (j + 1) * pic_width_in_ctbs / num_tile_cols; + *p++ = tmp - prev_w; + *p++ = h; + if (j == 0 && + (pps->column_width_minus1[0] + 1) == 1 && + ctb_size == 16) + no_chroma = 1; + prev_w = tmp; + } + } + } + } else { + hantro_reg_write(vpu, &g2_num_tile_rows, 1); + hantro_reg_write(vpu, &g2_num_tile_cols, 1); + + /* There's one tile, with dimensions equal to pic size. */ + p[0] = pic_width_in_ctbs; + p[1] = pic_height_in_ctbs; + } + + if (no_chroma) + vpu_debug(1, "%s: no chroma!\n", __func__); +} + +static int compute_header_skip_length(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; + int skip = 0; + + if (pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT) + /* size of pic_output_flag */ + skip++; + + if (sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) + /* size of pic_order_cnt_lsb */ + skip += 2; + + if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) { + /* size of pic_order_cnt_lsb */ + skip += sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + + /* size of short_term_ref_pic_set_sps_flag */ + skip++; + + if (decode_params->short_term_ref_pic_set_size) + /* size of st_ref_pic_set( num_short_term_ref_pic_sets ) */ + skip += decode_params->short_term_ref_pic_set_size; + else if (sps->num_short_term_ref_pic_sets > 1) + skip += fls(sps->num_short_term_ref_pic_sets - 1); + + skip += decode_params->long_term_ref_pic_set_size; + } + + return skip; +} + +static void set_params(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; + const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; + struct hantro_dev *vpu = ctx->dev; + u32 min_log2_cb_size, max_log2_ctb_size, min_cb_size, max_ctb_size; + u32 pic_width_in_min_cbs, pic_height_in_min_cbs; + u32 pic_width_aligned, pic_height_aligned; + u32 partial_ctb_x, partial_ctb_y; + + hantro_reg_write(vpu, &g2_bit_depth_y_minus8, sps->bit_depth_luma_minus8); + hantro_reg_write(vpu, &g2_bit_depth_c_minus8, sps->bit_depth_chroma_minus8); + + hantro_reg_write(vpu, &g2_output_8_bits, 0); + + hantro_reg_write(vpu, &g2_hdr_skip_length, compute_header_skip_length(ctx)); + + min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; + max_log2_ctb_size = min_log2_cb_size + sps->log2_diff_max_min_luma_coding_block_size; + + hantro_reg_write(vpu, &g2_min_cb_size, min_log2_cb_size); + hantro_reg_write(vpu, &g2_max_cb_size, max_log2_ctb_size); + + min_cb_size = 1 << min_log2_cb_size; + max_ctb_size = 1 << max_log2_ctb_size; + + pic_width_in_min_cbs = sps->pic_width_in_luma_samples / min_cb_size; + pic_height_in_min_cbs = sps->pic_height_in_luma_samples / min_cb_size; + pic_width_aligned = ALIGN(sps->pic_width_in_luma_samples, max_ctb_size); + pic_height_aligned = ALIGN(sps->pic_height_in_luma_samples, max_ctb_size); + + partial_ctb_x = !!(sps->pic_width_in_luma_samples != pic_width_aligned); + partial_ctb_y = !!(sps->pic_height_in_luma_samples != pic_height_aligned); + + hantro_reg_write(vpu, &g2_partial_ctb_x, partial_ctb_x); + hantro_reg_write(vpu, &g2_partial_ctb_y, partial_ctb_y); + + hantro_reg_write(vpu, &g2_pic_width_in_cbs, pic_width_in_min_cbs); + hantro_reg_write(vpu, &g2_pic_height_in_cbs, pic_height_in_min_cbs); + + hantro_reg_write(vpu, &g2_pic_width_4x4, + (pic_width_in_min_cbs * min_cb_size) / 4); + hantro_reg_write(vpu, &g2_pic_height_4x4, + (pic_height_in_min_cbs * min_cb_size) / 4); + + hantro_reg_write(vpu, &hevc_max_inter_hierdepth, + sps->max_transform_hierarchy_depth_inter); + hantro_reg_write(vpu, &hevc_max_intra_hierdepth, + sps->max_transform_hierarchy_depth_intra); + hantro_reg_write(vpu, &hevc_min_trb_size, + sps->log2_min_luma_transform_block_size_minus2 + 2); + hantro_reg_write(vpu, &hevc_max_trb_size, + sps->log2_min_luma_transform_block_size_minus2 + 2 + + sps->log2_diff_max_min_luma_transform_block_size); + + hantro_reg_write(vpu, &g2_tempor_mvp_e, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) && + !(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)); + hantro_reg_write(vpu, &g2_strong_smooth_e, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); + hantro_reg_write(vpu, &g2_asym_pred_e, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); + hantro_reg_write(vpu, &g2_sao_e, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); + hantro_reg_write(vpu, &g2_sign_data_hide, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); + + if (pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED) { + hantro_reg_write(vpu, &g2_cu_qpd_e, 1); + hantro_reg_write(vpu, &g2_max_cu_qpd_depth, pps->diff_cu_qp_delta_depth); + } else { + hantro_reg_write(vpu, &g2_cu_qpd_e, 0); + hantro_reg_write(vpu, &g2_max_cu_qpd_depth, 0); + } + + hantro_reg_write(vpu, &g2_cb_qp_offset, pps->pps_cb_qp_offset); + hantro_reg_write(vpu, &g2_cr_qp_offset, pps->pps_cr_qp_offset); + + hantro_reg_write(vpu, &g2_filt_offset_beta, pps->pps_beta_offset_div2); + hantro_reg_write(vpu, &g2_filt_offset_tc, pps->pps_tc_offset_div2); + hantro_reg_write(vpu, &g2_slice_hdr_ext_e, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); + hantro_reg_write(vpu, &g2_slice_hdr_ext_bits, pps->num_extra_slice_header_bits); + hantro_reg_write(vpu, &g2_slice_chqp_present, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); + hantro_reg_write(vpu, &g2_weight_bipr_idc, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); + hantro_reg_write(vpu, &g2_transq_bypass, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); + hantro_reg_write(vpu, &g2_list_mod_e, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); + hantro_reg_write(vpu, &g2_entropy_sync_e, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); + hantro_reg_write(vpu, &g2_cabac_init_present, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); + hantro_reg_write(vpu, &g2_idr_pic_e, + !!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC)); + hantro_reg_write(vpu, &hevc_parallel_merge, + pps->log2_parallel_merge_level_minus2 + 2); + hantro_reg_write(vpu, &g2_pcm_filt_d, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); + hantro_reg_write(vpu, &g2_pcm_e, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)); + if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) { + hantro_reg_write(vpu, &g2_max_pcm_size, + sps->log2_diff_max_min_pcm_luma_coding_block_size + + sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); + hantro_reg_write(vpu, &g2_min_pcm_size, + sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); + hantro_reg_write(vpu, &g2_bit_depth_pcm_y, + sps->pcm_sample_bit_depth_luma_minus1 + 1); + hantro_reg_write(vpu, &g2_bit_depth_pcm_c, + sps->pcm_sample_bit_depth_chroma_minus1 + 1); + } else { + hantro_reg_write(vpu, &g2_max_pcm_size, 0); + hantro_reg_write(vpu, &g2_min_pcm_size, 0); + hantro_reg_write(vpu, &g2_bit_depth_pcm_y, 0); + hantro_reg_write(vpu, &g2_bit_depth_pcm_c, 0); + } + + hantro_reg_write(vpu, &g2_start_code_e, 1); + hantro_reg_write(vpu, &g2_init_qp, pps->init_qp_minus26 + 26); + hantro_reg_write(vpu, &g2_weight_pred_e, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); + hantro_reg_write(vpu, &g2_cabac_init_present, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); + hantro_reg_write(vpu, &g2_const_intra_e, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + hantro_reg_write(vpu, &g2_transform_skip, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); + hantro_reg_write(vpu, &g2_out_filtering_dis, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); + hantro_reg_write(vpu, &g2_filt_ctrl_pres, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); + hantro_reg_write(vpu, &g2_dependent_slice, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); + hantro_reg_write(vpu, &g2_filter_override, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); + hantro_reg_write(vpu, &g2_refidx0_active, + pps->num_ref_idx_l0_default_active_minus1 + 1); + hantro_reg_write(vpu, &g2_refidx1_active, + pps->num_ref_idx_l1_default_active_minus1 + 1); + hantro_reg_write(vpu, &g2_apf_threshold, 8); +} + +static void set_ref_pic_list(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + struct hantro_dev *vpu = ctx->dev; + const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; + u32 list0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; + u32 list1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; + static const struct hantro_reg ref_pic_regs0[] = { + hevc_rlist_f0, + hevc_rlist_f1, + hevc_rlist_f2, + hevc_rlist_f3, + hevc_rlist_f4, + hevc_rlist_f5, + hevc_rlist_f6, + hevc_rlist_f7, + hevc_rlist_f8, + hevc_rlist_f9, + hevc_rlist_f10, + hevc_rlist_f11, + hevc_rlist_f12, + hevc_rlist_f13, + hevc_rlist_f14, + hevc_rlist_f15, + }; + static const struct hantro_reg ref_pic_regs1[] = { + hevc_rlist_b0, + hevc_rlist_b1, + hevc_rlist_b2, + hevc_rlist_b3, + hevc_rlist_b4, + hevc_rlist_b5, + hevc_rlist_b6, + hevc_rlist_b7, + hevc_rlist_b8, + hevc_rlist_b9, + hevc_rlist_b10, + hevc_rlist_b11, + hevc_rlist_b12, + hevc_rlist_b13, + hevc_rlist_b14, + hevc_rlist_b15, + }; + unsigned int i, j; + + /* List 0 contains: short term before, short term after and long term */ + j = 0; + for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list0); i++) + list0[j++] = decode_params->poc_st_curr_before[i]; + for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list0); i++) + list0[j++] = decode_params->poc_st_curr_after[i]; + for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list0); i++) + list0[j++] = decode_params->poc_lt_curr[i]; + + /* Fill the list, copying over and over */ + i = 0; + while (j < ARRAY_SIZE(list0)) + list0[j++] = list0[i++]; + + j = 0; + for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list1); i++) + list1[j++] = decode_params->poc_st_curr_after[i]; + for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list1); i++) + list1[j++] = decode_params->poc_st_curr_before[i]; + for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list1); i++) + list1[j++] = decode_params->poc_lt_curr[i]; + + i = 0; + while (j < ARRAY_SIZE(list1)) + list1[j++] = list1[i++]; + + for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { + hantro_reg_write(vpu, &ref_pic_regs0[i], list0[i]); + hantro_reg_write(vpu, &ref_pic_regs1[i], list1[i]); + } +} + +static int set_ref(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; + const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; + const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; + dma_addr_t luma_addr, chroma_addr, mv_addr = 0; + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *vb2_dst; + struct hantro_decoded_buffer *dst; + size_t cr_offset = hantro_hevc_chroma_offset(ctx); + size_t mv_offset = hantro_hevc_motion_vectors_offset(ctx); + u32 max_ref_frames; + u16 dpb_longterm_e; + static const struct hantro_reg cur_poc[] = { + hevc_cur_poc_00, + hevc_cur_poc_01, + hevc_cur_poc_02, + hevc_cur_poc_03, + hevc_cur_poc_04, + hevc_cur_poc_05, + hevc_cur_poc_06, + hevc_cur_poc_07, + hevc_cur_poc_08, + hevc_cur_poc_09, + hevc_cur_poc_10, + hevc_cur_poc_11, + hevc_cur_poc_12, + hevc_cur_poc_13, + hevc_cur_poc_14, + hevc_cur_poc_15, + }; + unsigned int i; + + max_ref_frames = decode_params->num_poc_lt_curr + + decode_params->num_poc_st_curr_before + + decode_params->num_poc_st_curr_after; + /* + * Set max_ref_frames to non-zero to avoid HW hang when decoding + * badly marked I-frames. + */ + max_ref_frames = max_ref_frames ? max_ref_frames : 1; + hantro_reg_write(vpu, &g2_num_ref_frames, max_ref_frames); + hantro_reg_write(vpu, &g2_filter_over_slices, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); + hantro_reg_write(vpu, &g2_filter_over_tiles, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); + + /* + * Write POC count diff from current pic. + */ + for (i = 0; i < decode_params->num_active_dpb_entries && i < ARRAY_SIZE(cur_poc); i++) { + char poc_diff = decode_params->pic_order_cnt_val - dpb[i].pic_order_cnt_val; + + hantro_reg_write(vpu, &cur_poc[i], poc_diff); + } + + if (i < ARRAY_SIZE(cur_poc)) { + /* + * After the references, fill one entry pointing to itself, + * i.e. difference is zero. + */ + hantro_reg_write(vpu, &cur_poc[i], 0); + i++; + } + + /* Fill the rest with the current picture */ + for (; i < ARRAY_SIZE(cur_poc); i++) + hantro_reg_write(vpu, &cur_poc[i], decode_params->pic_order_cnt_val); + + set_ref_pic_list(ctx); + + /* We will only keep the reference pictures that are still used */ + hantro_hevc_ref_init(ctx); + + /* Set up addresses of DPB buffers */ + dpb_longterm_e = 0; + for (i = 0; i < decode_params->num_active_dpb_entries && + i < (V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1); i++) { + luma_addr = hantro_hevc_get_ref_buf(ctx, dpb[i].pic_order_cnt_val); + if (!luma_addr) + return -ENOMEM; + + chroma_addr = luma_addr + cr_offset; + mv_addr = luma_addr + mv_offset; + + if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) + dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); + + hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); + hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); + hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); + } + + vb2_dst = hantro_get_dst_buf(ctx); + dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); + luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); + if (!luma_addr) + return -ENOMEM; + + if (hantro_hevc_add_ref_buf(ctx, decode_params->pic_order_cnt_val, luma_addr)) + return -EINVAL; + + chroma_addr = luma_addr + cr_offset; + mv_addr = luma_addr + mv_offset; + + hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); + hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); + hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr); + + hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr); + hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr); + hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr); + + for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { + hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0); + } + + hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e); + + return 0; +} + +static void set_buffers(struct hantro_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_buf; + struct hantro_dev *vpu = ctx->dev; + dma_addr_t src_dma; + u32 src_len, src_buf_len; + + src_buf = hantro_get_src_buf(ctx); + + /* Source (stream) buffer. */ + src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + src_buf_len = vb2_plane_size(&src_buf->vb2_buf, 0); + + hantro_write_addr(vpu, G2_STREAM_ADDR, src_dma); + hantro_reg_write(vpu, &g2_stream_len, src_len); + hantro_reg_write(vpu, &g2_strm_buffer_len, src_buf_len); + hantro_reg_write(vpu, &g2_strm_start_offset, 0); + hantro_reg_write(vpu, &g2_write_mvs_e, 1); + + hantro_write_addr(vpu, G2_TILE_SIZES_ADDR, ctx->hevc_dec.tile_sizes.dma); + hantro_write_addr(vpu, G2_TILE_FILTER_ADDR, ctx->hevc_dec.tile_filter.dma); + hantro_write_addr(vpu, G2_TILE_SAO_ADDR, ctx->hevc_dec.tile_sao.dma); + hantro_write_addr(vpu, G2_TILE_BSD_ADDR, ctx->hevc_dec.tile_bsd.dma); +} + +static void prepare_scaling_list_buffer(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_scaling_matrix *sc = ctrls->scaling; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + u8 *p = ((u8 *)ctx->hevc_dec.scaling_lists.cpu); + unsigned int scaling_list_enabled; + unsigned int i, j, k; + + scaling_list_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); + hantro_reg_write(vpu, &g2_scaling_list_e, scaling_list_enabled); + + if (!scaling_list_enabled) + return; + + for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_16x16); i++) + *p++ = sc->scaling_list_dc_coef_16x16[i]; + + for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_32x32); i++) + *p++ = sc->scaling_list_dc_coef_32x32[i]; + + /* 128-bit boundary */ + p += 8; + + /* write scaling lists column by column */ + + for (i = 0; i < 6; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 4; k++) + *p++ = sc->scaling_list_4x4[i][4 * k + j]; + + for (i = 0; i < 6; i++) + for (j = 0; j < 8; j++) + for (k = 0; k < 8; k++) + *p++ = sc->scaling_list_8x8[i][8 * k + j]; + + for (i = 0; i < 6; i++) + for (j = 0; j < 8; j++) + for (k = 0; k < 8; k++) + *p++ = sc->scaling_list_16x16[i][8 * k + j]; + + for (i = 0; i < 2; i++) + for (j = 0; j < 8; j++) + for (k = 0; k < 8; k++) + *p++ = sc->scaling_list_32x32[i][8 * k + j]; + + hantro_write_addr(vpu, G2_HEVC_SCALING_LIST_ADDR, ctx->hevc_dec.scaling_lists.dma); +} + +int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + int ret; + + hantro_g2_check_idle(vpu); + + /* Prepare HEVC decoder context. */ + ret = hantro_hevc_dec_prepare_run(ctx); + if (ret) + return ret; + + /* Configure hardware registers. */ + set_params(ctx); + + /* set reference pictures */ + ret = set_ref(ctx); + if (ret) + return ret; + + set_buffers(ctx); + prepare_tile_info_buffer(ctx); + + prepare_scaling_list_buffer(ctx); + + hantro_end_prepare_run(ctx); + + hantro_reg_write(vpu, &g2_mode, HEVC_DEC_MODE); + hantro_reg_write(vpu, &g2_clk_gate_e, 1); + + /* Don't disable output */ + hantro_reg_write(vpu, &g2_out_dis, 0); + + /* Don't compress buffers */ + hantro_reg_write(vpu, &g2_ref_compress_bypass, 1); + + /* Bus width and max burst */ + hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128); + hantro_reg_write(vpu, &g2_max_burst, 16); + + /* Swap */ + hantro_reg_write(vpu, &g2_strm_swap, 0xf); + hantro_reg_write(vpu, &g2_dirmv_swap, 0xf); + hantro_reg_write(vpu, &g2_compress_swap, 0xf); + + /* Start decoding! */ + vdpu_write(vpu, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h new file mode 100644 index 000000000000..82606783591a --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, Collabora + * + * Author: Benjamin Gaignard + */ + +#ifndef HANTRO_G2_REGS_H_ +#define HANTRO_G2_REGS_H_ + +#include "hantro.h" + +#define G2_SWREG(nr) ((nr) * 4) + +#define G2_DEC_REG(b, s, m) \ + ((const struct hantro_reg) { \ + .base = G2_SWREG(b), \ + .shift = s, \ + .mask = m, \ + }) + +#define G2_REG_VERSION G2_SWREG(0) + +#define G2_REG_INTERRUPT G2_SWREG(1) +#define G2_REG_INTERRUPT_DEC_RDY_INT BIT(12) +#define G2_REG_INTERRUPT_DEC_ABORT_E BIT(5) +#define G2_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) +#define G2_REG_INTERRUPT_DEC_E BIT(0) + +#define HEVC_DEC_MODE 0xc +#define VP9_DEC_MODE 0xd + +#define BUS_WIDTH_32 0 +#define BUS_WIDTH_64 1 +#define BUS_WIDTH_128 2 +#define BUS_WIDTH_256 3 + +#define g2_strm_swap G2_DEC_REG(2, 28, 0xf) +#define g2_strm_swap_old G2_DEC_REG(2, 27, 0x1f) +#define g2_pic_swap G2_DEC_REG(2, 22, 0x1f) +#define g2_dirmv_swap G2_DEC_REG(2, 20, 0xf) +#define g2_dirmv_swap_old G2_DEC_REG(2, 17, 0x1f) +#define g2_tab0_swap_old G2_DEC_REG(2, 12, 0x1f) +#define g2_tab1_swap_old G2_DEC_REG(2, 7, 0x1f) +#define g2_tab2_swap_old G2_DEC_REG(2, 2, 0x1f) + +#define g2_mode G2_DEC_REG(3, 27, 0x1f) +#define g2_compress_swap G2_DEC_REG(3, 20, 0xf) +#define g2_ref_compress_bypass G2_DEC_REG(3, 17, 0x1) +#define g2_out_rs_e G2_DEC_REG(3, 16, 0x1) +#define g2_out_dis G2_DEC_REG(3, 15, 0x1) +#define g2_out_filtering_dis G2_DEC_REG(3, 14, 0x1) +#define g2_write_mvs_e G2_DEC_REG(3, 12, 0x1) +#define g2_tab3_swap_old G2_DEC_REG(3, 7, 0x1f) +#define g2_rscan_swap G2_DEC_REG(3, 2, 0x1f) + +#define g2_pic_width_in_cbs G2_DEC_REG(4, 19, 0x1fff) +#define g2_pic_height_in_cbs G2_DEC_REG(4, 6, 0x1fff) +#define g2_num_ref_frames G2_DEC_REG(4, 0, 0x1f) + +#define g2_start_bit G2_DEC_REG(5, 25, 0x7f) +#define g2_scaling_list_e G2_DEC_REG(5, 24, 0x1) +#define g2_cb_qp_offset G2_DEC_REG(5, 19, 0x1f) +#define g2_cr_qp_offset G2_DEC_REG(5, 14, 0x1f) +#define g2_sign_data_hide G2_DEC_REG(5, 12, 0x1) +#define g2_tempor_mvp_e G2_DEC_REG(5, 11, 0x1) +#define g2_max_cu_qpd_depth G2_DEC_REG(5, 5, 0x3f) +#define g2_cu_qpd_e G2_DEC_REG(5, 4, 0x1) +#define g2_pix_shift G2_DEC_REG(5, 0, 0xf) + +#define g2_stream_len G2_DEC_REG(6, 0, 0xffffffff) + +#define g2_cabac_init_present G2_DEC_REG(7, 31, 0x1) +#define g2_weight_pred_e G2_DEC_REG(7, 28, 0x1) +#define g2_weight_bipr_idc G2_DEC_REG(7, 26, 0x3) +#define g2_filter_over_slices G2_DEC_REG(7, 25, 0x1) +#define g2_filter_over_tiles G2_DEC_REG(7, 24, 0x1) +#define g2_asym_pred_e G2_DEC_REG(7, 23, 0x1) +#define g2_sao_e G2_DEC_REG(7, 22, 0x1) +#define g2_pcm_filt_d G2_DEC_REG(7, 21, 0x1) +#define g2_slice_chqp_present G2_DEC_REG(7, 20, 0x1) +#define g2_dependent_slice G2_DEC_REG(7, 19, 0x1) +#define g2_filter_override G2_DEC_REG(7, 18, 0x1) +#define g2_strong_smooth_e G2_DEC_REG(7, 17, 0x1) +#define g2_filt_offset_beta G2_DEC_REG(7, 12, 0x1f) +#define g2_filt_offset_tc G2_DEC_REG(7, 7, 0x1f) +#define g2_slice_hdr_ext_e G2_DEC_REG(7, 6, 0x1) +#define g2_slice_hdr_ext_bits G2_DEC_REG(7, 3, 0x7) + +#define g2_const_intra_e G2_DEC_REG(8, 31, 0x1) +#define g2_filt_ctrl_pres G2_DEC_REG(8, 30, 0x1) +#define g2_bit_depth_y G2_DEC_REG(8, 21, 0xf) +#define g2_bit_depth_c G2_DEC_REG(8, 17, 0xf) +#define g2_idr_pic_e G2_DEC_REG(8, 16, 0x1) +#define g2_bit_depth_pcm_y G2_DEC_REG(8, 12, 0xf) +#define g2_bit_depth_pcm_c G2_DEC_REG(8, 8, 0xf) +#define g2_bit_depth_y_minus8 G2_DEC_REG(8, 6, 0x3) +#define g2_bit_depth_c_minus8 G2_DEC_REG(8, 4, 0x3) +#define g2_rs_out_bit_depth G2_DEC_REG(8, 4, 0xf) +#define g2_output_8_bits G2_DEC_REG(8, 3, 0x1) +#define g2_output_format G2_DEC_REG(8, 0, 0x7) +#define g2_pp_pix_shift G2_DEC_REG(8, 0, 0xf) + +#define g2_refidx1_active G2_DEC_REG(9, 19, 0x1f) +#define g2_refidx0_active G2_DEC_REG(9, 14, 0x1f) +#define g2_hdr_skip_length G2_DEC_REG(9, 0, 0x3fff) + +#define g2_start_code_e G2_DEC_REG(10, 31, 0x1) +#define g2_init_qp_old G2_DEC_REG(10, 25, 0x3f) +#define g2_init_qp G2_DEC_REG(10, 24, 0x7f) +#define g2_num_tile_cols_old G2_DEC_REG(10, 20, 0x1f) +#define g2_num_tile_cols G2_DEC_REG(10, 19, 0x1f) +#define g2_num_tile_rows_old G2_DEC_REG(10, 15, 0x1f) +#define g2_num_tile_rows G2_DEC_REG(10, 14, 0x1f) +#define g2_tile_e G2_DEC_REG(10, 1, 0x1) +#define g2_entropy_sync_e G2_DEC_REG(10, 0, 0x1) + +#define vp9_transform_mode G2_DEC_REG(11, 27, 0x7) +#define vp9_filt_sharpness G2_DEC_REG(11, 21, 0x7) +#define vp9_mcomp_filt_type G2_DEC_REG(11, 8, 0x7) +#define vp9_high_prec_mv_e G2_DEC_REG(11, 7, 0x1) +#define vp9_comp_pred_mode G2_DEC_REG(11, 4, 0x3) +#define vp9_gref_sign_bias G2_DEC_REG(11, 2, 0x1) +#define vp9_aref_sign_bias G2_DEC_REG(11, 0, 0x1) + +#define g2_refer_lterm_e G2_DEC_REG(12, 16, 0xffff) +#define g2_min_cb_size G2_DEC_REG(12, 13, 0x7) +#define g2_max_cb_size G2_DEC_REG(12, 10, 0x7) +#define g2_min_pcm_size G2_DEC_REG(12, 7, 0x7) +#define g2_max_pcm_size G2_DEC_REG(12, 4, 0x7) +#define g2_pcm_e G2_DEC_REG(12, 3, 0x1) +#define g2_transform_skip G2_DEC_REG(12, 2, 0x1) +#define g2_transq_bypass G2_DEC_REG(12, 1, 0x1) +#define g2_list_mod_e G2_DEC_REG(12, 0, 0x1) + +#define hevc_min_trb_size G2_DEC_REG(13, 13, 0x7) +#define hevc_max_trb_size G2_DEC_REG(13, 10, 0x7) +#define hevc_max_intra_hierdepth G2_DEC_REG(13, 7, 0x7) +#define hevc_max_inter_hierdepth G2_DEC_REG(13, 4, 0x7) +#define hevc_parallel_merge G2_DEC_REG(13, 0, 0xf) + +#define hevc_rlist_f0 G2_DEC_REG(14, 0, 0x1f) +#define hevc_rlist_f1 G2_DEC_REG(14, 10, 0x1f) +#define hevc_rlist_f2 G2_DEC_REG(14, 20, 0x1f) +#define hevc_rlist_b0 G2_DEC_REG(14, 5, 0x1f) +#define hevc_rlist_b1 G2_DEC_REG(14, 15, 0x1f) +#define hevc_rlist_b2 G2_DEC_REG(14, 25, 0x1f) + +#define hevc_rlist_f3 G2_DEC_REG(15, 0, 0x1f) +#define hevc_rlist_f4 G2_DEC_REG(15, 10, 0x1f) +#define hevc_rlist_f5 G2_DEC_REG(15, 20, 0x1f) +#define hevc_rlist_b3 G2_DEC_REG(15, 5, 0x1f) +#define hevc_rlist_b4 G2_DEC_REG(15, 15, 0x1f) +#define hevc_rlist_b5 G2_DEC_REG(15, 25, 0x1f) + +#define hevc_rlist_f6 G2_DEC_REG(16, 0, 0x1f) +#define hevc_rlist_f7 G2_DEC_REG(16, 10, 0x1f) +#define hevc_rlist_f8 G2_DEC_REG(16, 20, 0x1f) +#define hevc_rlist_b6 G2_DEC_REG(16, 5, 0x1f) +#define hevc_rlist_b7 G2_DEC_REG(16, 15, 0x1f) +#define hevc_rlist_b8 G2_DEC_REG(16, 25, 0x1f) + +#define hevc_rlist_f9 G2_DEC_REG(17, 0, 0x1f) +#define hevc_rlist_f10 G2_DEC_REG(17, 10, 0x1f) +#define hevc_rlist_f11 G2_DEC_REG(17, 20, 0x1f) +#define hevc_rlist_b9 G2_DEC_REG(17, 5, 0x1f) +#define hevc_rlist_b10 G2_DEC_REG(17, 15, 0x1f) +#define hevc_rlist_b11 G2_DEC_REG(17, 25, 0x1f) + +#define hevc_rlist_f12 G2_DEC_REG(18, 0, 0x1f) +#define hevc_rlist_f13 G2_DEC_REG(18, 10, 0x1f) +#define hevc_rlist_f14 G2_DEC_REG(18, 20, 0x1f) +#define hevc_rlist_b12 G2_DEC_REG(18, 5, 0x1f) +#define hevc_rlist_b13 G2_DEC_REG(18, 15, 0x1f) +#define hevc_rlist_b14 G2_DEC_REG(18, 25, 0x1f) + +#define hevc_rlist_f15 G2_DEC_REG(19, 0, 0x1f) +#define hevc_rlist_b15 G2_DEC_REG(19, 5, 0x1f) + +#define g2_partial_ctb_x G2_DEC_REG(20, 31, 0x1) +#define g2_partial_ctb_y G2_DEC_REG(20, 30, 0x1) +#define g2_pic_width_4x4 G2_DEC_REG(20, 16, 0xfff) +#define g2_pic_height_4x4 G2_DEC_REG(20, 0, 0xfff) + +#define vp9_qp_delta_y_dc G2_DEC_REG(13, 23, 0x3f) +#define vp9_qp_delta_ch_dc G2_DEC_REG(13, 17, 0x3f) +#define vp9_qp_delta_ch_ac G2_DEC_REG(13, 11, 0x3f) +#define vp9_last_sign_bias G2_DEC_REG(13, 10, 0x1) +#define vp9_lossless_e G2_DEC_REG(13, 9, 0x1) +#define vp9_comp_pred_var_ref1 G2_DEC_REG(13, 7, 0x3) +#define vp9_comp_pred_var_ref0 G2_DEC_REG(13, 5, 0x3) +#define vp9_comp_pred_fixed_ref G2_DEC_REG(13, 3, 0x3) +#define vp9_segment_temp_upd_e G2_DEC_REG(13, 2, 0x1) +#define vp9_segment_upd_e G2_DEC_REG(13, 1, 0x1) +#define vp9_segment_e G2_DEC_REG(13, 0, 0x1) + +#define vp9_filt_level G2_DEC_REG(14, 18, 0x3f) +#define vp9_refpic_seg0 G2_DEC_REG(14, 15, 0x7) +#define vp9_skip_seg0 G2_DEC_REG(14, 14, 0x1) +#define vp9_filt_level_seg0 G2_DEC_REG(14, 8, 0x3f) +#define vp9_quant_seg0 G2_DEC_REG(14, 0, 0xff) + +#define vp9_refpic_seg1 G2_DEC_REG(15, 15, 0x7) +#define vp9_skip_seg1 G2_DEC_REG(15, 14, 0x1) +#define vp9_filt_level_seg1 G2_DEC_REG(15, 8, 0x3f) +#define vp9_quant_seg1 G2_DEC_REG(15, 0, 0xff) + +#define vp9_refpic_seg2 G2_DEC_REG(16, 15, 0x7) +#define vp9_skip_seg2 G2_DEC_REG(16, 14, 0x1) +#define vp9_filt_level_seg2 G2_DEC_REG(16, 8, 0x3f) +#define vp9_quant_seg2 G2_DEC_REG(16, 0, 0xff) + +#define vp9_refpic_seg3 G2_DEC_REG(17, 15, 0x7) +#define vp9_skip_seg3 G2_DEC_REG(17, 14, 0x1) +#define vp9_filt_level_seg3 G2_DEC_REG(17, 8, 0x3f) +#define vp9_quant_seg3 G2_DEC_REG(17, 0, 0xff) + +#define vp9_refpic_seg4 G2_DEC_REG(18, 15, 0x7) +#define vp9_skip_seg4 G2_DEC_REG(18, 14, 0x1) +#define vp9_filt_level_seg4 G2_DEC_REG(18, 8, 0x3f) +#define vp9_quant_seg4 G2_DEC_REG(18, 0, 0xff) + +#define vp9_refpic_seg5 G2_DEC_REG(19, 15, 0x7) +#define vp9_skip_seg5 G2_DEC_REG(19, 14, 0x1) +#define vp9_filt_level_seg5 G2_DEC_REG(19, 8, 0x3f) +#define vp9_quant_seg5 G2_DEC_REG(19, 0, 0xff) + +#define hevc_cur_poc_00 G2_DEC_REG(46, 24, 0xff) +#define hevc_cur_poc_01 G2_DEC_REG(46, 16, 0xff) +#define hevc_cur_poc_02 G2_DEC_REG(46, 8, 0xff) +#define hevc_cur_poc_03 G2_DEC_REG(46, 0, 0xff) + +#define hevc_cur_poc_04 G2_DEC_REG(47, 24, 0xff) +#define hevc_cur_poc_05 G2_DEC_REG(47, 16, 0xff) +#define hevc_cur_poc_06 G2_DEC_REG(47, 8, 0xff) +#define hevc_cur_poc_07 G2_DEC_REG(47, 0, 0xff) + +#define hevc_cur_poc_08 G2_DEC_REG(48, 24, 0xff) +#define hevc_cur_poc_09 G2_DEC_REG(48, 16, 0xff) +#define hevc_cur_poc_10 G2_DEC_REG(48, 8, 0xff) +#define hevc_cur_poc_11 G2_DEC_REG(48, 0, 0xff) + +#define hevc_cur_poc_12 G2_DEC_REG(49, 24, 0xff) +#define hevc_cur_poc_13 G2_DEC_REG(49, 16, 0xff) +#define hevc_cur_poc_14 G2_DEC_REG(49, 8, 0xff) +#define hevc_cur_poc_15 G2_DEC_REG(49, 0, 0xff) + +#define vp9_refpic_seg6 G2_DEC_REG(31, 15, 0x7) +#define vp9_skip_seg6 G2_DEC_REG(31, 14, 0x1) +#define vp9_filt_level_seg6 G2_DEC_REG(31, 8, 0x3f) +#define vp9_quant_seg6 G2_DEC_REG(31, 0, 0xff) + +#define vp9_refpic_seg7 G2_DEC_REG(32, 15, 0x7) +#define vp9_skip_seg7 G2_DEC_REG(32, 14, 0x1) +#define vp9_filt_level_seg7 G2_DEC_REG(32, 8, 0x3f) +#define vp9_quant_seg7 G2_DEC_REG(32, 0, 0xff) + +#define vp9_lref_width G2_DEC_REG(33, 16, 0xffff) +#define vp9_lref_height G2_DEC_REG(33, 0, 0xffff) + +#define vp9_gref_width G2_DEC_REG(34, 16, 0xffff) +#define vp9_gref_height G2_DEC_REG(34, 0, 0xffff) + +#define vp9_aref_width G2_DEC_REG(35, 16, 0xffff) +#define vp9_aref_height G2_DEC_REG(35, 0, 0xffff) + +#define vp9_lref_hor_scale G2_DEC_REG(36, 16, 0xffff) +#define vp9_lref_ver_scale G2_DEC_REG(36, 0, 0xffff) + +#define vp9_gref_hor_scale G2_DEC_REG(37, 16, 0xffff) +#define vp9_gref_ver_scale G2_DEC_REG(37, 0, 0xffff) + +#define vp9_aref_hor_scale G2_DEC_REG(38, 16, 0xffff) +#define vp9_aref_ver_scale G2_DEC_REG(38, 0, 0xffff) + +#define vp9_filt_ref_adj_0 G2_DEC_REG(46, 24, 0x7f) +#define vp9_filt_ref_adj_1 G2_DEC_REG(46, 16, 0x7f) +#define vp9_filt_ref_adj_2 G2_DEC_REG(46, 8, 0x7f) +#define vp9_filt_ref_adj_3 G2_DEC_REG(46, 0, 0x7f) + +#define vp9_filt_mb_adj_0 G2_DEC_REG(47, 24, 0x7f) +#define vp9_filt_mb_adj_1 G2_DEC_REG(47, 16, 0x7f) +#define vp9_filt_mb_adj_2 G2_DEC_REG(47, 8, 0x7f) +#define vp9_filt_mb_adj_3 G2_DEC_REG(47, 0, 0x7f) + +#define g2_apf_threshold G2_DEC_REG(55, 0, 0xffff) + +#define g2_clk_gate_e G2_DEC_REG(58, 16, 0x1) +#define g2_double_buffer_e G2_DEC_REG(58, 15, 0x1) +#define g2_buswidth G2_DEC_REG(58, 8, 0x7) +#define g2_max_burst G2_DEC_REG(58, 0, 0xff) + +#define g2_down_scale_e G2_DEC_REG(184, 7, 0x1) +#define g2_down_scale_y G2_DEC_REG(184, 2, 0x3) +#define g2_down_scale_x G2_DEC_REG(184, 0, 0x3) + +#define G2_REG_CONFIG G2_SWREG(58) +#define G2_REG_CONFIG_DEC_CLK_GATE_E BIT(16) +#define G2_REG_CONFIG_DEC_CLK_GATE_IDLE_E BIT(17) + +#define G2_OUT_LUMA_ADDR (G2_SWREG(65)) +#define G2_REF_LUMA_ADDR(i) (G2_SWREG(67) + ((i) * 0x8)) +#define G2_VP9_SEGMENT_WRITE_ADDR (G2_SWREG(79)) +#define G2_VP9_SEGMENT_READ_ADDR (G2_SWREG(81)) +#define G2_OUT_CHROMA_ADDR (G2_SWREG(99)) +#define G2_REF_CHROMA_ADDR(i) (G2_SWREG(101) + ((i) * 0x8)) +#define G2_OUT_MV_ADDR (G2_SWREG(133)) +#define G2_REF_MV_ADDR(i) (G2_SWREG(135) + ((i) * 0x8)) +#define G2_TILE_SIZES_ADDR (G2_SWREG(167)) +#define G2_STREAM_ADDR (G2_SWREG(169)) +#define G2_HEVC_SCALING_LIST_ADDR (G2_SWREG(171)) +#define G2_VP9_CTX_COUNT_ADDR (G2_SWREG(171)) +#define G2_VP9_PROBS_ADDR (G2_SWREG(173)) +#define G2_RS_OUT_LUMA_ADDR (G2_SWREG(175)) +#define G2_RS_OUT_CHROMA_ADDR (G2_SWREG(177)) +#define G2_TILE_FILTER_ADDR (G2_SWREG(179)) +#define G2_TILE_SAO_ADDR (G2_SWREG(181)) +#define G2_TILE_BSD_ADDR (G2_SWREG(183)) +#define G2_DS_DST (G2_SWREG(186)) +#define G2_DS_DST_CHR (G2_SWREG(188)) + +#define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff) +#define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff) + +#endif diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c new file mode 100644 index 000000000000..6fc4b555517f --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c @@ -0,0 +1,1014 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VP9 codec driver + * + * Copyright (C) 2021 Collabora Ltd. + */ +#include "media/videobuf2-core.h" +#include "media/videobuf2-dma-contig.h" +#include "media/videobuf2-v4l2.h" +#include +#include +#include +#include + +#include "hantro.h" +#include "hantro_vp9.h" +#include "hantro_g2_regs.h" + +#define G2_ALIGN 16 + +enum hantro_ref_frames { + INTRA_FRAME = 0, + LAST_FRAME = 1, + GOLDEN_FRAME = 2, + ALTREF_FRAME = 3, + MAX_REF_FRAMES = 4 +}; + +static int start_prepare_run(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame **dec_params) +{ + const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + struct v4l2_ctrl *ctrl; + unsigned int fctx_idx; + + /* v4l2-specific stuff */ + hantro_start_prepare_run(ctx); + + ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_FRAME); + if (WARN_ON(!ctrl)) + return -EINVAL; + *dec_params = ctrl->p_cur.p; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); + if (WARN_ON(!ctrl)) + return -EINVAL; + prob_updates = ctrl->p_cur.p; + vp9_ctx->cur.tx_mode = prob_updates->tx_mode; + + /* + * vp9 stuff + * + * by this point the userspace has done all parts of 6.2 uncompressed_header() + * except this fragment: + * if ( FrameIsIntra || error_resilient_mode ) { + * setup_past_independence ( ) + * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 || + * reset_frame_context == 3 ) { + * for ( i = 0; i < 4; i ++ ) { + * save_probs( i ) + * } + * } else if ( reset_frame_context == 2 ) { + * save_probs( frame_context_idx ) + * } + * frame_context_idx = 0 + * } + */ + fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx->frame_context); + vp9_ctx->cur.frame_context_idx = fctx_idx; + + /* 6.1 frame(sz): load_probs() and load_probs2() */ + vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; + + /* + * The userspace has also performed 6.3 compressed_header(), but handling the + * probs in a special way. All probs which need updating, except MV-related, + * have been read from the bitstream and translated through inv_map_table[], + * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed + * by userspace are either translated values (there are no 0 values in + * inv_map_table[]), or zero to indicate no update. All MV-related probs which need + * updating have been read from the bitstream and (mv_prob << 1) | 1 has been + * performed. The values passed by userspace are either new values + * to replace old ones (the above mentioned shift and bitwise or never result in + * a zero) or zero to indicate no update. + * fw_update_probs() performs actual probs updates or leaves probs as-is + * for values for which a zero was passed from userspace. + */ + v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, *dec_params); + + return 0; +} + +static size_t chroma_offset(const struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2; + + return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel; +} + +static size_t mv_offset(const struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + size_t cr_offset = chroma_offset(ctx, dec_params); + + return ALIGN((cr_offset * 3) / 2, G2_ALIGN); +} + +static struct hantro_decoded_buffer * +get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf; + + /* + * If a ref is unused or invalid, address of current destination + * buffer is returned. + */ + buf = vb2_find_buffer(cap_q, timestamp); + if (!buf) + buf = &dst->vb2_buf; + + return vb2_to_hantro_decoded_buf(buf); +} + +static void update_dec_buf_info(struct hantro_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + buf->vp9.width = dec_params->frame_width_minus_1 + 1; + buf->vp9.height = dec_params->frame_height_minus_1 + 1; + buf->vp9.bit_depth = dec_params->bit_depth; +} + +static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx, + struct hantro_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + vp9_ctx->cur.valid = true; + vp9_ctx->cur.reference_mode = dec_params->reference_mode; + vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; + vp9_ctx->cur.flags = dec_params->flags; + vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; +} + +static void config_output(struct hantro_ctx *ctx, + struct hantro_decoded_buffer *dst, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + dma_addr_t luma_addr, chroma_addr, mv_addr; + + hantro_reg_write(ctx->dev, &g2_out_dis, 0); + if (!ctx->dev->variant->legacy_regs) + hantro_reg_write(ctx->dev, &g2_output_format, 0); + + luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); + hantro_write_addr(ctx->dev, G2_OUT_LUMA_ADDR, luma_addr); + + chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr); + + mv_addr = luma_addr + mv_offset(ctx, dec_params); + hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr); +} + +struct hantro_vp9_ref_reg { + const struct hantro_reg width; + const struct hantro_reg height; + const struct hantro_reg hor_scale; + const struct hantro_reg ver_scale; + u32 y_base; + u32 c_base; +}; + +static void config_ref(struct hantro_ctx *ctx, + struct hantro_decoded_buffer *dst, + const struct hantro_vp9_ref_reg *ref_reg, + const struct v4l2_ctrl_vp9_frame *dec_params, + u64 ref_ts) +{ + struct hantro_decoded_buffer *buf; + dma_addr_t luma_addr, chroma_addr; + u32 refw, refh; + + buf = get_ref_buf(ctx, &dst->base.vb, ref_ts); + refw = buf->vp9.width; + refh = buf->vp9.height; + + hantro_reg_write(ctx->dev, &ref_reg->width, refw); + hantro_reg_write(ctx->dev, &ref_reg->height, refh); + + hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst->vp9.width); + hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst->vp9.height); + + luma_addr = hantro_get_dec_buf_addr(ctx, &buf->base.vb.vb2_buf); + hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr); + + chroma_addr = luma_addr + chroma_offset(ctx, dec_params); + hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr); +} + +static void config_ref_registers(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params, + struct hantro_decoded_buffer *dst, + struct hantro_decoded_buffer *mv_ref) +{ + static const struct hantro_vp9_ref_reg ref_regs[] = { + { + /* Last */ + .width = vp9_lref_width, + .height = vp9_lref_height, + .hor_scale = vp9_lref_hor_scale, + .ver_scale = vp9_lref_ver_scale, + .y_base = G2_REF_LUMA_ADDR(0), + .c_base = G2_REF_CHROMA_ADDR(0), + }, { + /* Golden */ + .width = vp9_gref_width, + .height = vp9_gref_height, + .hor_scale = vp9_gref_hor_scale, + .ver_scale = vp9_gref_ver_scale, + .y_base = G2_REF_LUMA_ADDR(4), + .c_base = G2_REF_CHROMA_ADDR(4), + }, { + /* Altref */ + .width = vp9_aref_width, + .height = vp9_aref_height, + .hor_scale = vp9_aref_hor_scale, + .ver_scale = vp9_aref_ver_scale, + .y_base = G2_REF_LUMA_ADDR(5), + .c_base = G2_REF_CHROMA_ADDR(5), + }, + }; + dma_addr_t mv_addr; + + config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params->last_frame_ts); + config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params->golden_frame_ts); + config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts); + + mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) + + mv_offset(ctx, dec_params); + hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr); + + hantro_reg_write(ctx->dev, &vp9_last_sign_bias, + dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0); + + hantro_reg_write(ctx->dev, &vp9_gref_sign_bias, + dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0); + + hantro_reg_write(ctx->dev, &vp9_aref_sign_bias, + dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0); +} + +static void recompute_tile_info(unsigned short *tile_info, unsigned int tiles, unsigned int sbs) +{ + int i; + unsigned int accumulated = 0; + unsigned int next_accumulated; + + for (i = 1; i <= tiles; ++i) { + next_accumulated = i * sbs / tiles; + *tile_info++ = next_accumulated - accumulated; + accumulated = next_accumulated; + } +} + +static void +recompute_tile_rc_info(struct hantro_ctx *ctx, + unsigned int tile_r, unsigned int tile_c, + unsigned int sbs_r, unsigned int sbs_c) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + + recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r); + recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c); + + vp9_ctx->last_tile_r = tile_r; + vp9_ctx->last_tile_c = tile_c; + vp9_ctx->last_sbs_r = sbs_r; + vp9_ctx->last_sbs_c = sbs_c; +} + +static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int sbs_r) +{ + if (tile_r == sbs_r + 1) + return 1; + + if (tile_r == sbs_r + 2) + return 2; + + return 0; +} + +static void +fill_tile_info(struct hantro_ctx *ctx, + unsigned int tile_r, unsigned int tile_c, + unsigned int sbs_r, unsigned int sbs_c, + unsigned short *tile_mem) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + unsigned int i, j; + bool first = true; + + for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) { + unsigned short r_info = vp9_ctx->tile_r_info[i]; + + if (first) { + if (i > 0) + r_info += vp9_ctx->tile_r_info[0]; + if (i == 2) + r_info += vp9_ctx->tile_r_info[1]; + first = false; + } + for (j = 0; j < tile_c; ++j) { + *tile_mem++ = vp9_ctx->tile_c_info[j]; + *tile_mem++ = r_info; + } + } +} + +static void +config_tiles(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params, + struct hantro_decoded_buffer *dst) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + struct hantro_aux_buf *misc = &vp9_ctx->misc; + struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge; + dma_addr_t addr; + unsigned short *tile_mem; + unsigned int rows, cols; + + addr = misc->dma + vp9_ctx->tile_info_offset; + hantro_write_addr(ctx->dev, G2_TILE_SIZES_ADDR, addr); + + tile_mem = misc->cpu + vp9_ctx->tile_info_offset; + if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) { + unsigned int tile_r = (1 << dec_params->tile_rows_log2); + unsigned int tile_c = (1 << dec_params->tile_cols_log2); + unsigned int sbs_r = hantro_vp9_num_sbs(dst->vp9.height); + unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width); + + if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx->last_tile_c || + sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx->last_sbs_c) + recompute_tile_rc_info(ctx, tile_r, tile_c, sbs_r, sbs_c); + + fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, tile_mem); + + cols = tile_c; + rows = tile_r; + hantro_reg_write(ctx->dev, &g2_tile_e, 1); + } else { + tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width); + tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height); + + cols = 1; + rows = 1; + hantro_reg_write(ctx->dev, &g2_tile_e, 0); + } + + if (ctx->dev->variant->legacy_regs) { + hantro_reg_write(ctx->dev, &g2_num_tile_cols_old, cols); + hantro_reg_write(ctx->dev, &g2_num_tile_rows_old, rows); + } else { + hantro_reg_write(ctx->dev, &g2_num_tile_cols, cols); + hantro_reg_write(ctx->dev, &g2_num_tile_rows, rows); + } + + /* provide aux buffers even if no tiles are used */ + addr = tile_edge->dma; + hantro_write_addr(ctx->dev, G2_TILE_FILTER_ADDR, addr); + + addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset; + hantro_write_addr(ctx->dev, G2_TILE_BSD_ADDR, addr); +} + +static void +update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx, + const struct v4l2_vp9_segmentation *seg, + unsigned int feature, + unsigned int segid) +{ + u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature); + + vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid][feature]; + vp9_ctx->feature_enabled[segid] &= ~mask; + vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & mask); +} + +static inline s16 clip3(s16 x, s16 y, s16 z) +{ + return (z < x) ? x : (z > y) ? y : z; +} + +static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 clip) +{ + if (absolute) + return feature_data; + + return clip3(0, 255, feat_val + feature_data); +} + +static void config_segment(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + const struct v4l2_vp9_segmentation *seg; + s16 feat_val; + unsigned char feat_id; + unsigned int segid; + bool segment_enabled, absolute, update_data; + + static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = { + { vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, vp9_skip_seg0 }, + { vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, vp9_skip_seg1 }, + { vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, vp9_skip_seg2 }, + { vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, vp9_skip_seg3 }, + { vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, vp9_skip_seg4 }, + { vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, vp9_skip_seg5 }, + { vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, vp9_skip_seg6 }, + { vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, vp9_skip_seg7 }, + }; + + segment_enabled = !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED); + hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled); + hantro_reg_write(ctx->dev, &vp9_segment_upd_e, + !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP)); + hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e, + !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE)); + + seg = &dec_params->seg; + absolute = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE); + update_data = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA); + + for (segid = 0; segid < 8; ++segid) { + /* Quantizer segment feature */ + feat_id = V4L2_VP9_SEG_LVL_ALT_Q; + feat_val = dec_params->quant.base_q_idx; + if (segment_enabled) { + if (update_data) + update_feat_and_flag(vp9_ctx, seg, feat_id, segid); + if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) + feat_val = feat_val_clip3(feat_val, + vp9_ctx->feature_data[segid][feat_id], + absolute, 255); + } + hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); + + /* Loop filter segment feature */ + feat_id = V4L2_VP9_SEG_LVL_ALT_L; + feat_val = dec_params->lf.level; + if (segment_enabled) { + if (update_data) + update_feat_and_flag(vp9_ctx, seg, feat_id, segid); + if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) + feat_val = feat_val_clip3(feat_val, + vp9_ctx->feature_data[segid][feat_id], + absolute, 63); + } + hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); + + /* Reference frame segment feature */ + feat_id = V4L2_VP9_SEG_LVL_REF_FRAME; + feat_val = 0; + if (segment_enabled) { + if (update_data) + update_feat_and_flag(vp9_ctx, seg, feat_id, segid); + if (!(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && + v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) + feat_val = vp9_ctx->feature_data[segid][feat_id] + 1; + } + hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); + + /* Skip segment feature */ + feat_id = V4L2_VP9_SEG_LVL_SKIP; + feat_val = 0; + if (segment_enabled) { + if (update_data) + update_feat_and_flag(vp9_ctx, seg, feat_id, segid); + feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, + feat_id, segid) ? 1 : 0; + } + hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); + } +} + +static void config_loop_filter(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) +{ + bool d = dec_params->lf.flags & V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED; + + hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level); + hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params->lf.level == 0); + hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params->lf.sharpness); + + hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params->lf.ref_deltas[0] : 0); + hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params->lf.ref_deltas[1] : 0); + hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params->lf.ref_deltas[2] : 0); + hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params->lf.ref_deltas[3] : 0); + hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params->lf.mode_deltas[0] : 0); + hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params->lf.mode_deltas[1] : 0); +} + +static void config_picture_dimensions(struct hantro_ctx *ctx, struct hantro_decoded_buffer *dst) +{ + u32 pic_w_4x4, pic_h_4x4; + + hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 7) / 8); + hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height + 7) / 8); + pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2; + pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2; + hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4); + hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4); +} + +static void +config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) +{ + if (ctx->dev->variant->legacy_regs) { + hantro_reg_write(ctx->dev, &g2_bit_depth_y, dec_params->bit_depth); + hantro_reg_write(ctx->dev, &g2_bit_depth_c, dec_params->bit_depth); + hantro_reg_write(ctx->dev, &g2_pix_shift, 0); + } else { + hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params->bit_depth - 8); + hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params->bit_depth - 8); + } +} + +static inline bool is_lossless(const struct v4l2_vp9_quantization *quant) +{ + return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 && + quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0; +} + +static void +config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) +{ + hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params->quant.delta_q_y_dc); + hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params->quant.delta_q_uv_dc); + hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params->quant.delta_q_uv_ac); + hantro_reg_write(ctx->dev, &vp9_lossless_e, is_lossless(&dec_params->quant)); +} + +static u32 +hantro_interp_filter_from_v4l2(unsigned int interpolation_filter) +{ + switch (interpolation_filter) { + case V4L2_VP9_INTERP_FILTER_EIGHTTAP: + return 0x1; + case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH: + return 0; + case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP: + return 0x2; + case V4L2_VP9_INTERP_FILTER_BILINEAR: + return 0x3; + case V4L2_VP9_INTERP_FILTER_SWITCHABLE: + return 0x4; + } + + return 0; +} + +static void +config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, + bool intra_only, bool resolution_change) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + + hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only); + + hantro_reg_write(ctx->dev, &vp9_transform_mode, vp9_ctx->cur.tx_mode); + + hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ? + 0 : hantro_interp_filter_from_v4l2(dec_params->interpolation_filter)); + + hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e, + !!(dec_params->flags & V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV)); + + hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params->reference_mode); + + hantro_reg_write(ctx->dev, &g2_tempor_mvp_e, + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && + !(vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_INTRA_ONLY) && + !resolution_change && + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME + ); + + hantro_reg_write(ctx->dev, &g2_write_mvs_e, + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME)); +} + +static void +config_compound_reference(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + u32 comp_fixed_ref, comp_var_ref[2]; + bool last_ref_frame_sign_bias; + bool golden_ref_frame_sign_bias; + bool alt_ref_frame_sign_bias; + bool comp_ref_allowed = 0; + + comp_fixed_ref = 0; + comp_var_ref[0] = 0; + comp_var_ref[1] = 0; + + last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST; + golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN; + alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT; + + /* 6.3.12 Frame reference mode syntax */ + comp_ref_allowed |= golden_ref_frame_sign_bias != last_ref_frame_sign_bias; + comp_ref_allowed |= alt_ref_frame_sign_bias != last_ref_frame_sign_bias; + + if (comp_ref_allowed) { + if (last_ref_frame_sign_bias == + golden_ref_frame_sign_bias) { + comp_fixed_ref = ALTREF_FRAME; + comp_var_ref[0] = LAST_FRAME; + comp_var_ref[1] = GOLDEN_FRAME; + } else if (last_ref_frame_sign_bias == + alt_ref_frame_sign_bias) { + comp_fixed_ref = GOLDEN_FRAME; + comp_var_ref[0] = LAST_FRAME; + comp_var_ref[1] = ALTREF_FRAME; + } else { + comp_fixed_ref = LAST_FRAME; + comp_var_ref[0] = GOLDEN_FRAME; + comp_var_ref[1] = ALTREF_FRAME; + } + } + + hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref); + hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, comp_var_ref[0]); + hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, comp_var_ref[1]); +} + +#define INNER_LOOP \ +do { \ + for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) { \ + memcpy(adaptive->coef[i][j][k][l][m], \ + probs->coef[i][j][k][l][m], \ + sizeof(probs->coef[i][j][k][l][m])); \ + \ + adaptive->coef[i][j][k][l][m][3] = 0; \ + } \ +} while (0) + +static void config_probs(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + struct hantro_aux_buf *misc = &vp9_ctx->misc; + struct hantro_g2_all_probs *all_probs = misc->cpu; + struct hantro_g2_probs *adaptive; + struct hantro_g2_mv_probs *mv; + const struct v4l2_vp9_segmentation *seg = &dec_params->seg; + const struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; + int i, j, k, l, m; + + for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i) + for (j = 0; j < ARRAY_SIZE(all_probs->kf_y_mode_prob[0]); ++j) { + memcpy(all_probs->kf_y_mode_prob[i][j], + v4l2_vp9_kf_y_mode_prob[i][j], + ARRAY_SIZE(all_probs->kf_y_mode_prob[i][j])); + + all_probs->kf_y_mode_prob_tail[i][j][0] = + v4l2_vp9_kf_y_mode_prob[i][j][8]; + } + + memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs, + sizeof(all_probs->mb_segment_tree_probs)); + + memcpy(all_probs->segment_pred_probs, seg->pred_probs, + sizeof(all_probs->segment_pred_probs)); + + for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) { + memcpy(all_probs->kf_uv_mode_prob[i], v4l2_vp9_kf_uv_mode_prob[i], + ARRAY_SIZE(all_probs->kf_uv_mode_prob[i])); + + all_probs->kf_uv_mode_prob_tail[i][0] = v4l2_vp9_kf_uv_mode_prob[i][8]; + } + + adaptive = &all_probs->probs; + + for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) { + memcpy(adaptive->inter_mode[i], probs->inter_mode[i], + ARRAY_SIZE(probs->inter_mode[i])); + + adaptive->inter_mode[i][3] = 0; + } + + memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive->is_inter)); + + for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) { + memcpy(adaptive->uv_mode[i], probs->uv_mode[i], + sizeof(adaptive->uv_mode[i])); + adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8]; + } + + memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8)); + memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16)); + memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32)); + + for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) { + memcpy(adaptive->y_mode[i], probs->y_mode[i], + ARRAY_SIZE(adaptive->y_mode[i])); + + adaptive->y_mode_tail[i][0] = probs->y_mode[i][8]; + } + + for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) { + memcpy(adaptive->partition[0][i], v4l2_vp9_kf_partition_probs[i], + sizeof(v4l2_vp9_kf_partition_probs[i])); + + adaptive->partition[0][i][3] = 0; + } + + for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) { + memcpy(adaptive->partition[1][i], probs->partition[i], + sizeof(probs->partition[i])); + + adaptive->partition[1][i][3] = 0; + } + + memcpy(adaptive->interp_filter, probs->interp_filter, + sizeof(adaptive->interp_filter)); + + memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive->comp_mode)); + + memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip)); + + mv = &adaptive->mv; + + memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint)); + memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign)); + memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv->class0_bit)); + memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr)); + memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp)); + memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp)); + memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes)); + memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr)); + memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits)); + + memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive->single_ref)); + + memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive->comp_ref)); + + for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i) + for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j) + for (k = 0; k < ARRAY_SIZE(adaptive->coef[0][0]); ++k) + for (l = 0; l < ARRAY_SIZE(adaptive->coef[0][0][0]); ++l) + INNER_LOOP; + + hantro_write_addr(ctx->dev, G2_VP9_PROBS_ADDR, misc->dma); +} + +static void config_counts(struct hantro_ctx *ctx) +{ + struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; + struct hantro_aux_buf *misc = &vp9_dec->misc; + dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset; + + hantro_write_addr(ctx->dev, G2_VP9_CTX_COUNT_ADDR, addr); +} + +static void config_seg_map(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params, + bool intra_only, bool update_map) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map; + dma_addr_t addr; + + if (intra_only || + (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) { + memset(segment_map->cpu, 0, segment_map->size); + memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx->feature_data)); + memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx->feature_enabled)); + } + + addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx->segment_map_size; + hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_READ_ADDR, addr); + + addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx->segment_map_size; + hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_WRITE_ADDR, addr); + + if (update_map) + vp9_ctx->active_segment = 1 - vp9_ctx->active_segment; +} + +static void +config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, + struct vb2_v4l2_buffer *vb2_src) +{ + dma_addr_t stream_base, tmp_addr; + unsigned int headres_size; + u32 src_len, start_bit, src_buf_len; + + headres_size = dec_params->uncompressed_header_size + + dec_params->compressed_header_size; + + stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); + + tmp_addr = stream_base + headres_size; + if (ctx->dev->variant->legacy_regs) + hantro_write_addr(ctx->dev, G2_STREAM_ADDR, (tmp_addr & ~0xf)); + else + hantro_write_addr(ctx->dev, G2_STREAM_ADDR, stream_base); + + start_bit = (tmp_addr & 0xf) * 8; + hantro_reg_write(ctx->dev, &g2_start_bit, start_bit); + + src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0); + src_len += start_bit / 8 - headres_size; + hantro_reg_write(ctx->dev, &g2_stream_len, src_len); + + if (!ctx->dev->variant->legacy_regs) { + tmp_addr &= ~0xf; + hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - stream_base); + src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0); + hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len); + } +} + +static void +config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, + struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer *vb2_dst) +{ + struct hantro_decoded_buffer *dst, *last, *mv_ref; + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + const struct v4l2_vp9_segmentation *seg; + bool intra_only, resolution_change; + + /* vp9 stuff */ + dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); + + if (vp9_ctx->last.valid) + last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp); + else + last = dst; + + update_dec_buf_info(dst, dec_params); + update_ctx_cur_info(vp9_ctx, dst, dec_params); + seg = &dec_params->seg; + + intra_only = !!(dec_params->flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | + V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); + + if (!intra_only && + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && + vp9_ctx->last.valid) + mv_ref = last; + else + mv_ref = dst; + + resolution_change = dst->vp9.width != last->vp9.width || + dst->vp9.height != last->vp9.height; + + /* configure basic registers */ + hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE); + if (!ctx->dev->variant->legacy_regs) { + hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf); + hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf); + hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf); + hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1); + } else { + hantro_reg_write(ctx->dev, &g2_strm_swap_old, 0x1f); + hantro_reg_write(ctx->dev, &g2_pic_swap, 0x10); + hantro_reg_write(ctx->dev, &g2_dirmv_swap_old, 0x10); + hantro_reg_write(ctx->dev, &g2_tab0_swap_old, 0x10); + hantro_reg_write(ctx->dev, &g2_tab1_swap_old, 0x10); + hantro_reg_write(ctx->dev, &g2_tab2_swap_old, 0x10); + hantro_reg_write(ctx->dev, &g2_tab3_swap_old, 0x10); + hantro_reg_write(ctx->dev, &g2_rscan_swap, 0x10); + } + hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128); + hantro_reg_write(ctx->dev, &g2_max_burst, 16); + hantro_reg_write(ctx->dev, &g2_apf_threshold, 8); + hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1); + hantro_reg_write(ctx->dev, &g2_max_cb_size, 6); + hantro_reg_write(ctx->dev, &g2_min_cb_size, 3); + if (ctx->dev->variant->double_buffer) + hantro_reg_write(ctx->dev, &g2_double_buffer_e, 1); + + config_output(ctx, dst, dec_params); + + if (!intra_only) + config_ref_registers(ctx, dec_params, dst, mv_ref); + + config_tiles(ctx, dec_params, dst); + config_segment(ctx, dec_params); + config_loop_filter(ctx, dec_params); + config_picture_dimensions(ctx, dst); + config_bit_depth(ctx, dec_params); + config_quant(ctx, dec_params); + config_others(ctx, dec_params, intra_only, resolution_change); + config_compound_reference(ctx, dec_params); + config_probs(ctx, dec_params); + config_counts(ctx); + config_seg_map(ctx, dec_params, intra_only, + seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP); + config_source(ctx, dec_params, vb2_src); +} + +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx) +{ + const struct v4l2_ctrl_vp9_frame *decode_params; + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; + int ret; + + hantro_g2_check_idle(ctx->dev); + + ret = start_prepare_run(ctx, &decode_params); + if (ret) { + hantro_end_prepare_run(ctx); + return ret; + } + + src = hantro_get_src_buf(ctx); + dst = hantro_get_dst_buf(ctx); + + config_registers(ctx, decode_params, src, dst); + + hantro_end_prepare_run(ctx); + + vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); + + return 0; +} + +#define copy_tx_and_skip(p1, p2) \ +do { \ + memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ + memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ + memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ + memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ +} while (0) + +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + unsigned int fctx_idx; + + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) + goto out_update_last; + + fctx_idx = vp9_ctx->cur.frame_context_idx; + + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { + /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ + struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; + bool frame_is_intra = vp9_ctx->cur.flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); + struct tx_and_skip { + u8 tx8[2][1]; + u8 tx16[2][2]; + u8 tx32[2][3]; + u8 skip[3]; + } _tx_skip, *tx_skip = &_tx_skip; + struct v4l2_vp9_frame_symbol_counts *counts; + struct symbol_counts *hantro_cnts; + u32 tx16p[2][4]; + int i; + + /* buffer the forward-updated TX and skip probs */ + if (frame_is_intra) + copy_tx_and_skip(tx_skip, probs); + + /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ + *probs = vp9_ctx->frame_context[fctx_idx]; + + /* if FrameIsIntra then undo the effect of load_probs2() */ + if (frame_is_intra) + copy_tx_and_skip(probs, tx_skip); + + counts = &vp9_ctx->cnts; + hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; + for (i = 0; i < ARRAY_SIZE(tx16p); ++i) { + memcpy(tx16p[i], + hantro_cnts->tx16x16_count[i], + sizeof(hantro_cnts->tx16x16_count[0])); + tx16p[i][3] = 0; + } + counts->tx16p = &tx16p; + + v4l2_vp9_adapt_coef_probs(probs, counts, + !vp9_ctx->last.valid || + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, + frame_is_intra); + + if (!frame_is_intra) { + /* load_probs2() already done */ + u32 mv_mode[7][4]; + + for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) { + mv_mode[i][0] = hantro_cnts->inter_mode_counts[i][1][0]; + mv_mode[i][1] = hantro_cnts->inter_mode_counts[i][2][0]; + mv_mode[i][2] = hantro_cnts->inter_mode_counts[i][0][0]; + mv_mode[i][3] = hantro_cnts->inter_mode_counts[i][2][1]; + } + counts->mv_mode = &mv_mode; + v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, + vp9_ctx->cur.reference_mode, + vp9_ctx->cur.interpolation_filter, + vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); + } + } + + vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; + +out_update_last: + vp9_ctx->last = vp9_ctx->cur; +} diff --git a/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c new file mode 100644 index 000000000000..12d69503d6ba --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include "hantro_jpeg.h" +#include "hantro.h" +#include "hantro_v4l2.h" +#include "hantro_hw.h" +#include "hantro_h1_regs.h" + +#define H1_JPEG_QUANT_TABLE_COUNT 16 + +static void hantro_h1_set_src_img_ctrl(struct hantro_dev *vpu, + struct hantro_ctx *ctx) +{ + u32 overfill_r, overfill_b; + u32 reg; + + /* + * The format width and height are already macroblock aligned + * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination + * format width and height can be further modified by + * .vidioc_s_selection(), and the width is 4-aligned. + */ + overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; + overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; + + reg = H1_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width) + | H1_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) + | H1_REG_IN_IMG_CTRL_OVRFLB(overfill_b) + | H1_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); + vepu_write_relaxed(vpu, reg, H1_REG_IN_IMG_CTRL); +} + +static void hantro_h1_jpeg_enc_set_buffers(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; + dma_addr_t src[3]; + u32 size_left; + + size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; + if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) + size_left = 0; + + WARN_ON(pix_fmt->num_planes > 3); + + vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + ctx->vpu_dst_fmt->header_size, + H1_REG_ADDR_OUTPUT_STREAM); + vepu_write_relaxed(vpu, size_left, H1_REG_STR_BUF_LIMIT); + + if (pix_fmt->num_planes == 1) { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + /* single plane formats we supported are all interlaced */ + vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); + } else if (pix_fmt->num_planes == 2) { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); + vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); + vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); + } else { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); + src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); + vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); + vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); + vepu_write_relaxed(vpu, src[2], H1_REG_ADDR_IN_PLANE_2); + } +} + +static void +hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, + unsigned char *luma_qtable, + unsigned char *chroma_qtable) +{ + u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ + for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&luma_qtable_p[i]); + vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); + } + + for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); + vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); + } +} + +int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct hantro_jpeg_ctx jpeg_ctx; + u32 reg; + + src_buf = hantro_get_src_buf(ctx); + dst_buf = hantro_get_dst_buf(ctx); + + hantro_start_prepare_run(ctx); + + memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); + 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; + hantro_jpeg_header_assemble(&jpeg_ctx); + + /* Switch to JPEG encoder mode before writing registers */ + vepu_write_relaxed(vpu, H1_REG_ENC_CTRL_ENC_MODE_JPEG, + H1_REG_ENC_CTRL); + + hantro_h1_set_src_img_ctrl(vpu, ctx); + hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf); + hantro_h1_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, + jpeg_ctx.hw_chroma_qtable); + + reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 + | H1_REG_AXI_CTRL_INPUT_SWAP16 + | H1_REG_AXI_CTRL_BURST_LEN(16) + | H1_REG_AXI_CTRL_OUTPUT_SWAP32 + | H1_REG_AXI_CTRL_INPUT_SWAP32 + | H1_REG_AXI_CTRL_OUTPUT_SWAP8 + | H1_REG_AXI_CTRL_INPUT_SWAP8; + /* Make sure that all registers are written at this point. */ + vepu_write(vpu, reg, H1_REG_AXI_CTRL); + + reg = H1_REG_ENC_CTRL_WIDTH(MB_WIDTH(ctx->src_fmt.width)) + | H1_REG_ENC_CTRL_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) + | H1_REG_ENC_CTRL_ENC_MODE_JPEG + | H1_REG_ENC_PIC_INTRA + | H1_REG_ENC_CTRL_EN_BIT; + + hantro_end_prepare_run(ctx); + + vepu_write(vpu, reg, H1_REG_ENC_CTRL); + + return 0; +} + +void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; + struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + ctx->vpu_dst_fmt->header_size + bytesused); +} diff --git a/drivers/media/platform/verisilicon/hantro_h1_regs.h b/drivers/media/platform/verisilicon/hantro_h1_regs.h new file mode 100644 index 000000000000..30e7e7b920b5 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_h1_regs.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright 2018 Google LLC. + * Tomasz Figa + */ + +#ifndef HANTRO_H1_REGS_H_ +#define HANTRO_H1_REGS_H_ + +/* Encoder registers. */ +#define H1_REG_INTERRUPT 0x004 +#define H1_REG_INTERRUPT_FRAME_RDY BIT(2) +#define H1_REG_INTERRUPT_DIS_BIT BIT(1) +#define H1_REG_INTERRUPT_BIT BIT(0) +#define H1_REG_AXI_CTRL 0x008 +#define H1_REG_AXI_CTRL_OUTPUT_SWAP16 BIT(15) +#define H1_REG_AXI_CTRL_INPUT_SWAP16 BIT(14) +#define H1_REG_AXI_CTRL_BURST_LEN(x) ((x) << 8) +#define H1_REG_AXI_CTRL_GATE_BIT BIT(4) +#define H1_REG_AXI_CTRL_OUTPUT_SWAP32 BIT(3) +#define H1_REG_AXI_CTRL_INPUT_SWAP32 BIT(2) +#define H1_REG_AXI_CTRL_OUTPUT_SWAP8 BIT(1) +#define H1_REG_AXI_CTRL_INPUT_SWAP8 BIT(0) +#define H1_REG_ADDR_OUTPUT_STREAM 0x014 +#define H1_REG_ADDR_OUTPUT_CTRL 0x018 +#define H1_REG_ADDR_REF_LUMA 0x01c +#define H1_REG_ADDR_REF_CHROMA 0x020 +#define H1_REG_ADDR_REC_LUMA 0x024 +#define H1_REG_ADDR_REC_CHROMA 0x028 +#define H1_REG_ADDR_IN_PLANE_0 0x02c +#define H1_REG_ADDR_IN_PLANE_1 0x030 +#define H1_REG_ADDR_IN_PLANE_2 0x034 +#define H1_REG_ENC_CTRL 0x038 +#define H1_REG_ENC_CTRL_TIMEOUT_EN BIT(31) +#define H1_REG_ENC_CTRL_NAL_MODE_BIT BIT(29) +#define H1_REG_ENC_CTRL_WIDTH(w) ((w) << 19) +#define H1_REG_ENC_CTRL_HEIGHT(h) ((h) << 10) +#define H1_REG_ENC_PIC_INTER (0x0 << 3) +#define H1_REG_ENC_PIC_INTRA (0x1 << 3) +#define H1_REG_ENC_PIC_MVCINTER (0x2 << 3) +#define H1_REG_ENC_CTRL_ENC_MODE_H264 (0x3 << 1) +#define H1_REG_ENC_CTRL_ENC_MODE_JPEG (0x2 << 1) +#define H1_REG_ENC_CTRL_ENC_MODE_VP8 (0x1 << 1) +#define H1_REG_ENC_CTRL_EN_BIT BIT(0) +#define H1_REG_IN_IMG_CTRL 0x03c +#define H1_REG_IN_IMG_CTRL_ROW_LEN(x) ((x) << 12) +#define H1_REG_IN_IMG_CTRL_OVRFLR_D4(x) ((x) << 10) +#define H1_REG_IN_IMG_CTRL_OVRFLB(x) ((x) << 6) +#define H1_REG_IN_IMG_CTRL_FMT(x) ((x) << 2) +#define H1_REG_ENC_CTRL0 0x040 +#define H1_REG_ENC_CTRL0_INIT_QP(x) ((x) << 26) +#define H1_REG_ENC_CTRL0_SLICE_ALPHA(x) ((x) << 22) +#define H1_REG_ENC_CTRL0_SLICE_BETA(x) ((x) << 18) +#define H1_REG_ENC_CTRL0_CHROMA_QP_OFFSET(x) ((x) << 13) +#define H1_REG_ENC_CTRL0_FILTER_DIS(x) ((x) << 5) +#define H1_REG_ENC_CTRL0_IDR_PICID(x) ((x) << 1) +#define H1_REG_ENC_CTRL0_CONSTR_INTRA_PRED BIT(0) +#define H1_REG_ENC_CTRL1 0x044 +#define H1_REG_ENC_CTRL1_PPS_ID(x) ((x) << 24) +#define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16) +#define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x)) +#define H1_REG_ENC_CTRL2 0x048 +#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30) +#define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23) +#define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22) +#define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21) +#define H1_REG_ENC_CTRL2_CABAC_INIT_IDC(x) ((x) << 19) +#define H1_REG_ENC_CTRL2_ENTROPY_CODING_MODE BIT(18) +#define H1_REG_ENC_CTRL2_H264_INTER4X4_MODE BIT(17) +#define H1_REG_ENC_CTRL2_H264_STREAM_MODE BIT(16) +#define H1_REG_ENC_CTRL2_INTRA16X16_MODE(x) ((x)) +#define H1_REG_ENC_CTRL3 0x04c +#define H1_REG_ENC_CTRL3_MUTIMV_EN BIT(30) +#define H1_REG_ENC_CTRL3_MV_PENALTY_1_4P(x) ((x) << 20) +#define H1_REG_ENC_CTRL3_MV_PENALTY_4P(x) ((x) << 10) +#define H1_REG_ENC_CTRL3_MV_PENALTY_1P(x) ((x)) +#define H1_REG_ENC_CTRL4 0x050 +#define H1_REG_ENC_CTRL4_MV_PENALTY_16X8_8X16(x) ((x) << 20) +#define H1_REG_ENC_CTRL4_MV_PENALTY_8X8(x) ((x) << 10) +#define H1_REG_ENC_CTRL4_8X4_4X8(x) ((x)) +#define H1_REG_ENC_CTRL5 0x054 +#define H1_REG_ENC_CTRL5_MACROBLOCK_PENALTY(x) ((x) << 24) +#define H1_REG_ENC_CTRL5_COMPLETE_SLICES(x) ((x) << 16) +#define H1_REG_ENC_CTRL5_INTER_MODE(x) ((x)) +#define H1_REG_STR_HDR_REM_MSB 0x058 +#define H1_REG_STR_HDR_REM_LSB 0x05c +#define H1_REG_STR_BUF_LIMIT 0x060 +#define H1_REG_MAD_CTRL 0x064 +#define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28) +#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22) +#define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x)) +#define H1_REG_ADDR_VP8_PROB_CNT 0x068 +#define H1_REG_QP_VAL 0x06c +#define H1_REG_QP_VAL_LUM(x) ((x) << 26) +#define H1_REG_QP_VAL_MAX(x) ((x) << 20) +#define H1_REG_QP_VAL_MIN(x) ((x) << 14) +#define H1_REG_QP_VAL_CHECKPOINT_DISTAN(x) ((x)) +#define H1_REG_VP8_QP_VAL(i) (0x06c + ((i) * 0x4)) +#define H1_REG_CHECKPOINT(i) (0x070 + ((i) * 0x4)) +#define H1_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) +#define H1_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) +#define H1_REG_CHECKPOINT_RESULT(x) ((((x) >> (16 - 16 \ + * (i & 1))) & 0xffff) \ + * 32) +#define H1_REG_CHKPT_WORD_ERR(i) (0x084 + ((i) * 0x4)) +#define H1_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) +#define H1_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) +#define H1_REG_VP8_BOOL_ENC 0x08c +#define H1_REG_CHKPT_DELTA_QP 0x090 +#define H1_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) +#define H1_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) +#define H1_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) +#define H1_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) +#define H1_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) +#define H1_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) +#define H1_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) +#define H1_REG_VP8_CTRL0 0x090 +#define H1_REG_RLC_CTRL 0x094 +#define H1_REG_RLC_CTRL_STR_OFFS_SHIFT 23 +#define H1_REG_RLC_CTRL_STR_OFFS_MASK (0x3f << 23) +#define H1_REG_RLC_CTRL_RLC_SUM(x) ((x)) +#define H1_REG_MB_CTRL 0x098 +#define H1_REG_MB_CNT_OUT(x) (((x) & 0xffff)) +#define H1_REG_MB_CNT_SET(x) (((x) & 0xffff) << 16) +#define H1_REG_ADDR_NEXT_PIC 0x09c +#define H1_REG_JPEG_LUMA_QUAT(i) (0x100 + ((i) * 0x4)) +#define H1_REG_JPEG_CHROMA_QUAT(i) (0x140 + ((i) * 0x4)) +#define H1_REG_STABILIZATION_OUTPUT 0x0A0 +#define H1_REG_ADDR_CABAC_TBL 0x0cc +#define H1_REG_ADDR_MV_OUT 0x0d0 +#define H1_REG_RGB_YUV_COEFF(i) (0x0d4 + ((i) * 0x4)) +#define H1_REG_RGB_MASK_MSB 0x0dc +#define H1_REG_INTRA_AREA_CTRL 0x0e0 +#define H1_REG_CIR_INTRA_CTRL 0x0e4 +#define H1_REG_INTRA_SLICE_BITMAP(i) (0x0e8 + ((i) * 0x4)) +#define H1_REG_ADDR_VP8_DCT_PART(i) (0x0e8 + ((i) * 0x4)) +#define H1_REG_FIRST_ROI_AREA 0x0f0 +#define H1_REG_SECOND_ROI_AREA 0x0f4 +#define H1_REG_MVC_CTRL 0x0f8 +#define H1_REG_MVC_CTRL_MV16X16_FAVOR(x) ((x) << 28) +#define H1_REG_VP8_INTRA_PENALTY(i) (0x100 + ((i) * 0x4)) +#define H1_REG_ADDR_VP8_SEG_MAP 0x11c +#define H1_REG_VP8_SEG_QP(i) (0x120 + ((i) * 0x4)) +#define H1_REG_DMV_4P_1P_PENALTY(i) (0x180 + ((i) * 0x4)) +#define H1_REG_DMV_4P_1P_PENALTY_BIT(x, i) ((x) << (i) * 8) +#define H1_REG_DMV_QPEL_PENALTY(i) (0x200 + ((i) * 0x4)) +#define H1_REG_DMV_QPEL_PENALTY_BIT(x, i) ((x) << (i) * 8) +#define H1_REG_VP8_CTRL1 0x280 +#define H1_REG_VP8_BIT_COST_GOLDEN 0x284 +#define H1_REG_VP8_LOOP_FLT_DELTA(i) (0x288 + ((i) * 0x4)) + +#endif /* HANTRO_H1_REGS_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_h264.c b/drivers/media/platform/verisilicon/hantro_h264.c new file mode 100644 index 000000000000..4e9a0ecf5c13 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_h264.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip RK3288 VPU codec driver + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * Herman Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + */ + +#include +#include +#include + +#include "hantro.h" +#include "hantro_hw.h" + +/* Size with u32 units. */ +#define CABAC_INIT_BUFFER_SIZE (460 * 2) +#define POC_BUFFER_SIZE 34 +#define SCALING_LIST_SIZE (6 * 16 + 2 * 64) + +/* + * For valid and long term reference marking, index are reversed, so bit 31 + * indicates the status of the picture 0. + */ +#define REF_BIT(i) BIT(32 - 1 - (i)) + +/* Data structure describing auxiliary buffer format. */ +struct hantro_h264_dec_priv_tbl { + u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; + u32 poc[POC_BUFFER_SIZE]; + u8 scaling_list[SCALING_LIST_SIZE]; +}; + +/* + * Constant CABAC table. + * From drivers/media/platform/rk3288-vpu/rk3288_vpu_hw_h264d.c + * in https://chromium.googlesource.com/chromiumos/third_party/kernel, + * chromeos-3.14 branch. + */ +static const u32 h264_cabac_table[] = { + 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07330000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x000b0137, + 0x0045ef7f, 0xf3660052, 0xf94aeb6b, 0xe57fe17f, 0xe87fee5f, 0xe57feb72, + 0xe27fef7b, 0xf473f07a, 0xf573f43f, 0xfe44f154, 0xf368fd46, 0xf85df65a, + 0xe27fff4a, 0xfa61f95b, 0xec7ffc38, 0xfb52f94c, 0xea7df95d, 0xf557fd4d, + 0xfb47fc3f, 0xfc44f454, 0xf93ef941, 0x083d0538, 0xfe420140, 0x003dfe4e, + 0x01320734, 0x0a23002c, 0x0b26012d, 0x002e052c, 0x1f110133, 0x07321c13, + 0x10210e3e, 0xf36cf164, 0xf365f35b, 0xf45ef658, 0xf054f656, 0xf953f357, + 0xed5e0146, 0x0048fb4a, 0x123bf866, 0xf164005f, 0xfc4b0248, 0xf54bfd47, + 0x0f2ef345, 0x003e0041, 0x1525f148, 0x09391036, 0x003e0c48, 0x18000f09, + 0x08190d12, 0x0f090d13, 0x0a250c12, 0x061d1421, 0x0f1e042d, 0x013a003e, + 0x073d0c26, 0x0b2d0f27, 0x0b2a0d2c, 0x102d0c29, 0x0a311e22, 0x122a0a37, + 0x1133112e, 0x00591aed, 0x16ef1aef, 0x1ee71cec, 0x21e925e5, 0x21e928e4, + 0x26ef21f5, 0x28f129fa, 0x26012911, 0x1efa1b03, 0x1a1625f0, 0x23fc26f8, + 0x26fd2503, 0x26052a00, 0x23102716, 0x0e301b25, 0x153c0c44, 0x0261fd47, + 0xfa2afb32, 0xfd36fe3e, 0x003a013f, 0xfe48ff4a, 0xf75bfb43, 0xfb1bfd27, + 0xfe2c002e, 0xf040f844, 0xf64efa4d, 0xf656f45c, 0xf137f63c, 0xfa3efc41, + 0xf449f84c, 0xf950f758, 0xef6ef561, 0xec54f54f, 0xfa49fc4a, 0xf356f360, + 0xf561ed75, 0xf84efb21, 0xfc30fe35, 0xfd3ef347, 0xf64ff456, 0xf35af261, + 0x0000fa5d, 0xfa54f84f, 0x0042ff47, 0x003efe3c, 0xfe3bfb4b, 0xfd3efc3a, + 0xf742ff4f, 0x00470344, 0x0a2cf93e, 0x0f240e28, 0x101b0c1d, 0x012c1424, + 0x1220052a, 0x01300a3e, 0x112e0940, 0xf468f561, 0xf060f958, 0xf855f955, + 0xf755f358, 0x0442fd4d, 0xfd4cfa4c, 0x0a3aff4c, 0xff53f963, 0xf25f025f, + 0x004cfb4a, 0x0046f54b, 0x01440041, 0xf249033e, 0x043eff44, 0xf34b0b37, + 0x05400c46, 0x0f060613, 0x07100c0e, 0x120d0d0b, 0x0d0f0f10, 0x0c170d17, + 0x0f140e1a, 0x0e2c1128, 0x112f1811, 0x15151916, 0x1f1b161d, 0x13230e32, + 0x0a39073f, 0xfe4dfc52, 0xfd5e0945, 0xf46d24dd, 0x24de20e6, 0x25e22ce0, + 0x22ee22f1, 0x28f121f9, 0x23fb2100, 0x2602210d, 0x17230d3a, 0x1dfd1a00, + 0x161e1ff9, 0x23f122fd, 0x220324ff, 0x2205200b, 0x2305220c, 0x270b1e1d, + 0x221a1d27, 0x13421f15, 0x1f1f1932, 0xef78ec70, 0xee72f555, 0xf15cf259, + 0xe647f151, 0xf2500044, 0xf246e838, 0xe944e832, 0xf54a17f3, 0x1af328f1, + 0x31f22c03, 0x2d062c22, 0x21361352, 0xfd4bff17, 0x0122012b, 0x0036fe37, + 0x003d0140, 0x0044f75c, 0xf26af361, 0xf15af45a, 0xee58f649, 0xf74ff256, + 0xf649f646, 0xf645fb42, 0xf740fb3a, 0x023b15f6, 0x18f51cf8, 0x1cff1d03, + 0x1d092314, 0x1d240e43, 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, + 0xfa35ff36, 0x07331721, 0x17021500, 0x01090031, 0xdb760539, 0xf34ef541, + 0x013e0c31, 0xfc491132, 0x1240092b, 0x1d001a43, 0x105a0968, 0xd27fec68, + 0x0143f34e, 0xf541013e, 0xfa56ef5f, 0xfa3d092d, 0xfd45fa51, 0xf5600637, + 0x0743fb56, 0x0258003a, 0xfd4cf65e, 0x05360445, 0xfd510058, 0xf943fb4a, + 0xfc4afb50, 0xf948013a, 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, + 0x0d29033e, 0x002dfc4e, 0xfd60e57e, 0xe462e765, 0xe943e452, 0xec5ef053, + 0xea6eeb5b, 0xee66f35d, 0xe37ff95c, 0xfb59f960, 0xf36cfd2e, 0xff41ff39, + 0xf75dfd4a, 0xf75cf857, 0xe97e0536, 0x063c063b, 0x0645ff30, 0x0044fc45, + 0xf858fe55, 0xfa4eff4b, 0xf94d0236, 0x0532fd44, 0x0132062a, 0xfc51013f, + 0xfc460043, 0x0239fe4c, 0x0b230440, 0x013d0b23, 0x12190c18, 0x0d1d0d24, + 0xf65df949, 0xfe490d2e, 0x0931f964, 0x09350235, 0x0535fe3d, 0x00380038, + 0xf33ffb3c, 0xff3e0439, 0xfa450439, 0x0e270433, 0x0d440340, 0x013d093f, + 0x07321027, 0x052c0434, 0x0b30fb3c, 0xff3b003b, 0x1621052c, 0x0e2bff4e, + 0x003c0945, 0x0b1c0228, 0x032c0031, 0x002e022c, 0x0233002f, 0x0427023e, + 0x062e0036, 0x0336023a, 0x043f0633, 0x06390735, 0x06340637, 0x0b2d0e24, + 0x0835ff52, 0x0737fd4e, 0x0f2e161f, 0xff541907, 0x1ef91c03, 0x1c042000, + 0x22ff1e06, 0x1e062009, 0x1f131a1b, 0x1a1e2514, 0x1c221146, 0x0143053b, + 0x0943101e, 0x12201223, 0x161d181f, 0x1726122b, 0x14290b3f, 0x093b0940, + 0xff5efe59, 0xf76cfa4c, 0xfe2c002d, 0x0034fd40, 0xfe3bfc46, 0xfc4bf852, + 0xef66f74d, 0x0318002a, 0x00300037, 0xfa3bf947, 0xf453f557, 0xe277013a, + 0xfd1dff24, 0x0126022b, 0xfa37003a, 0x0040fd4a, 0xf65a0046, 0xfc1d051f, + 0x072a013b, 0xfe3afd48, 0xfd51f561, 0x003a0805, 0x0a0e0e12, 0x0d1b0228, + 0x003afd46, 0xfa4ff855, 0x0000f36a, 0xf06af657, 0xeb72ee6e, 0xf262ea6e, + 0xeb6aee67, 0xeb6be96c, 0xe670f660, 0xf45ffb5b, 0xf75dea5e, 0xfb560943, + 0xfc50f655, 0xff46073c, 0x093a053d, 0x0c320f32, 0x12311136, 0x0a29072e, + 0xff330731, 0x08340929, 0x062f0237, 0x0d290a2c, 0x06320535, 0x0d31043f, + 0x0640fe45, 0xfe3b0646, 0x0a2c091f, 0x0c2b0335, 0x0e220a26, 0xfd340d28, + 0x1120072c, 0x07260d32, 0x0a391a2b, 0x0e0b0b0e, 0x090b120b, 0x150917fe, + 0x20f120f1, 0x22eb27e9, 0x2adf29e1, 0x2ee426f4, 0x151d2de8, 0x35d330e6, + 0x41d52bed, 0x27f61e09, 0x121a141b, 0x0039f252, 0xfb4bed61, 0xdd7d1b00, + 0x1c001ffc, 0x1b062208, 0x1e0a1816, 0x21131620, 0x1a1f1529, 0x1a2c172f, + 0x10410e47, 0x083c063f, 0x11411518, 0x17141a17, 0x1b201c17, 0x1c181728, + 0x18201c1d, 0x172a1339, 0x1635163d, 0x0b560c28, 0x0b330e3b, 0xfc4ff947, + 0xfb45f746, 0xf842f644, 0xed49f445, 0xf046f143, 0xec3eed46, 0xf042ea41, + 0xec3f09fe, 0x1af721f7, 0x27f929fe, 0x2d033109, 0x2d1b243b, 0xfa42f923, + 0xf92af82d, 0xfb30f438, 0xfa3cfb3e, 0xf842f84c, 0xfb55fa51, 0xf64df951, + 0xef50ee49, 0xfc4af653, 0xf747f743, 0xff3df842, 0xf242003b, 0x023b15f3, + 0x21f227f9, 0x2efe3302, 0x3c063d11, 0x37222a3e, 0x14f10236, 0x034a14f1, + 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331619, 0x22001000, 0xfe090429, + 0xe3760241, 0xfa47f34f, 0x05340932, 0xfd460a36, 0x1a221316, 0x28003902, + 0x29241a45, 0xd37ff165, 0xfc4cfa47, 0xf34f0534, 0x0645f35a, 0x0034082b, + 0xfe45fb52, 0xf660023b, 0x024bfd57, 0xfd640138, 0xfd4afa55, 0x003bfd51, + 0xf956fb5f, 0xff42ff4d, 0x0146fe56, 0xfb48003d, 0x0029003f, 0x003f003f, + 0xf7530456, 0x0061f948, 0x0d29033e, 0x0d0f0733, 0x0250d97f, 0xee5bef60, + 0xe651dd62, 0xe866e961, 0xe577e863, 0xeb6eee66, 0xdc7f0050, 0xfb59f95e, + 0xfc5c0027, 0x0041f154, 0xdd7ffe49, 0xf468f75b, 0xe17f0337, 0x07380737, + 0x083dfd35, 0x0044f94a, 0xf758f367, 0xf35bf759, 0xf25cf84c, 0xf457e96e, + 0xe869f64e, 0xec70ef63, 0xb27fba7f, 0xce7fd27f, 0xfc42fb4e, 0xfc47f848, + 0x023bff37, 0xf946fa4b, 0xf859de77, 0xfd4b2014, 0x1e16d47f, 0x0036fb3d, + 0x003aff3c, 0xfd3df843, 0xe754f24a, 0xfb410534, 0x0239003d, 0xf745f546, + 0x1237fc47, 0x003a073d, 0x09291219, 0x0920052b, 0x092f002c, 0x0033022e, + 0x1326fc42, 0x0f260c2a, 0x09220059, 0x042d0a1c, 0x0a1f21f5, 0x34d5120f, + 0x1c0023ea, 0x26e72200, 0x27ee20f4, 0x66a20000, 0x38f121fc, 0x1d0a25fb, + 0x33e327f7, 0x34de45c6, 0x43c12cfb, 0x200737e3, 0x20010000, 0x1b2421e7, + 0x22e224e4, 0x26e426e5, 0x22ee23f0, 0x22f220f8, 0x25fa2300, 0x1e0a1c12, + 0x1a191d29, 0x004b0248, 0x084d0e23, 0x121f1123, 0x151e112d, 0x142a122d, + 0x1b1a1036, 0x07421038, 0x0b490a43, 0xf674e970, 0xf147f93d, 0x0035fb42, + 0xf54df750, 0xf754f657, 0xde7feb65, 0xfd27fb35, 0xf93df54b, 0xf14def5b, + 0xe76be76f, 0xe47af54c, 0xf62cf634, 0xf639f73a, 0xf048f945, 0xfc45fb4a, + 0xf7560242, 0xf7220120, 0x0b1f0534, 0xfe37fe43, 0x0049f859, 0x03340704, + 0x0a081108, 0x10130325, 0xff3dfb49, 0xff46fc4e, 0x0000eb7e, 0xe97cec6e, + 0xe67ee77c, 0xef69e579, 0xe575ef66, 0xe675e574, 0xdf7af65f, 0xf264f85f, + 0xef6fe472, 0xfa59fe50, 0xfc52f755, 0xf851ff48, 0x05400143, 0x09380045, + 0x01450745, 0xf945fa43, 0xf04dfe40, 0x023dfa43, 0xfd400239, 0xfd41fd42, + 0x003e0933, 0xff42fe47, 0xfe4bff46, 0xf7480e3c, 0x1025002f, 0x12230b25, + 0x0c290a29, 0x02300c29, 0x0d29003b, 0x03321328, 0x03421232, 0x13fa12fa, + 0x0e001af4, 0x1ff021e7, 0x21ea25e4, 0x27e22ae2, 0x2fd62ddc, 0x31de29ef, + 0x200945b9, 0x3fc142c0, 0x4db636d9, 0x34dd29f6, 0x240028ff, 0x1e0e1c1a, + 0x17250c37, 0x0b4125df, 0x27dc28db, 0x26e22edf, 0x2ae228e8, 0x31e326f4, + 0x28f626fd, 0x2efb1f14, 0x1d1e192c, 0x0c300b31, 0x1a2d1616, 0x17161b15, + 0x21141a1c, 0x1e181b22, 0x122a1927, 0x12320c46, 0x15360e47, 0x0b531920, + 0x15311536, 0xfb55fa51, 0xf64df951, 0xef50ee49, 0xfc4af653, 0xf747f743, + 0xff3df842, 0xf242003b, 0x023b11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, + 0x421b2f39, 0xfb470018, 0xff24fe2a, 0xfe34f739, 0xfa3ffc41, 0xfc43f952, + 0xfd51fd4c, 0xf948fa4e, 0xf448f244, 0xfd46fa4c, 0xfb42fb3e, 0x0039fc3d, + 0xf73c0136, 0x023a11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, 0x421b2f39, + 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331d10, + 0x19000e00, 0xf633fd3e, 0xe5631a10, 0xfc55e866, 0x05390639, 0xef490e39, + 0x1428140a, 0x1d003600, 0x252a0c61, 0xe07fea75, 0xfe4afc55, 0xe8660539, + 0xfa5df258, 0xfa2c0437, 0xf559f167, 0xeb741339, 0x143a0454, 0x0660013f, + 0xfb55f36a, 0x053f064b, 0xfd5aff65, 0x0337fc4f, 0xfe4bf461, 0xf932013c, + 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x0722f758, + 0xec7fdc7f, 0xef5bf25f, 0xe754e756, 0xf459ef5b, 0xe17ff24c, 0xee67f35a, + 0xdb7f0b50, 0x054c0254, 0x054efa37, 0x043df253, 0xdb7ffb4f, 0xf568f55b, + 0xe27f0041, 0xfe4f0048, 0xfc5cfa38, 0x0344f847, 0xf362fc56, 0xf458fb52, + 0xfd48fc43, 0xf848f059, 0xf745ff3b, 0x05420439, 0xfc47fe47, 0x023aff4a, + 0xfc2cff45, 0x003ef933, 0xfc2ffa2a, 0xfd29fa35, 0x084cf74e, 0xf5530934, + 0x0043fb5a, 0x0143f148, 0xfb4bf850, 0xeb53eb40, 0xf31fe740, 0xe35e094b, + 0x113ff84a, 0xfb23fe1b, 0x0d5b0341, 0xf945084d, 0xf642033e, 0xfd44ec51, + 0x001e0107, 0xfd17eb4a, 0x1042e97c, 0x11252cee, 0x32deea7f, 0x0427002a, + 0x07220b1d, 0x081f0625, 0x072a0328, 0x08210d2b, 0x0d24042f, 0x0337023a, + 0x063c082c, 0x0b2c0e2a, 0x07300438, 0x04340d25, 0x0931133a, 0x0a300c2d, + 0x00451421, 0x083f23ee, 0x21e71cfd, 0x180a1b00, 0x22f234d4, 0x27e81311, + 0x1f19241d, 0x1821220f, 0x1e141649, 0x1422131f, 0x1b2c1310, 0x0f240f24, + 0x151c1915, 0x1e141f0c, 0x1b10182a, 0x005d0e38, 0x0f391a26, 0xe87fe873, + 0xea52f73e, 0x0035003b, 0xf255f359, 0xf35ef55c, 0xe37feb64, 0xf239f443, + 0xf547f64d, 0xeb55f058, 0xe968f162, 0xdb7ff652, 0xf830f83d, 0xf842f946, + 0xf24bf64f, 0xf753f45c, 0xee6cfc4f, 0xea45f04b, 0xfe3a013a, 0xf34ef753, + 0xfc51f363, 0xf351fa26, 0xf33efa3a, 0xfe3bf049, 0xf64cf356, 0xf753f657, + 0x0000ea7f, 0xe77fe778, 0xe57fed72, 0xe975e776, 0xe675e871, 0xe476e178, + 0xdb7cf65e, 0xf166f663, 0xf36ace7f, 0xfb5c1139, 0xfb56f35e, 0xf45bfe4d, + 0x0047ff49, 0x0440f951, 0x05400f39, 0x01430044, 0xf6430144, 0x004d0240, + 0x0044fb4e, 0x0737053b, 0x02410e36, 0x0f2c053c, 0x0246fe4c, 0xee560c46, + 0x0540f446, 0x0b370538, 0x00450241, 0xfa4a0536, 0x0736fa4c, 0xf552fe4d, + 0xfe4d192a, 0x11f310f7, 0x11f41beb, 0x25e229d8, 0x2ad730d1, 0x27e02ed8, + 0x34cd2ed7, 0x34d92bed, 0x200b3dc9, 0x38d23ece, 0x51bd2dec, 0x23fe1c0f, + 0x22012701, 0x1e111426, 0x122d0f36, 0x004f24f0, 0x25f225ef, 0x2001220f, + 0x1d0f1819, 0x22161f10, 0x23121f1c, 0x2129241c, 0x1b2f153e, 0x121f131a, + 0x24181817, 0x1b10181e, 0x1f1d1629, 0x162a103c, 0x0f340e3c, 0x034ef07b, + 0x15351638, 0x193d1521, 0x1332113d, 0xfd4ef84a, 0xf748f648, 0xee4bf447, + 0xf53ffb46, 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, + 0x21ff2107, 0x1f0c2517, 0x1f261440, 0xf747f925, 0xf82cf531, 0xf638f43b, + 0xf83ff743, 0xfa44f64f, 0xfd4ef84a, 0xf748f648, 0xee4bf447, 0xf53ffb46, + 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, 0x21ff2107, + 0x1f0c2517, 0x1f261440 +}; + +static void +assemble_scaling_list(struct hantro_ctx *ctx) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; + const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; + const size_t num_list_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4); + const size_t list_len_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4[0]); + const size_t list_len_8x8 = ARRAY_SIZE(scaling->scaling_list_8x8[0]); + struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; + u32 *dst = (u32 *)tbl->scaling_list; + const u32 *src; + int i, j; + + if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) + return; + + for (i = 0; i < num_list_4x4; i++) { + src = (u32 *)&scaling->scaling_list_4x4[i]; + for (j = 0; j < list_len_4x4 / 4; j++) + *dst++ = swab32(src[j]); + } + + /* Only Intra/Inter Y lists */ + for (i = 0; i < 2; i++) { + src = (u32 *)&scaling->scaling_list_8x8[i]; + for (j = 0; j < list_len_8x8 / 4; j++) + *dst++ = swab32(src[j]); + } +} + +static void prepare_table(struct hantro_ctx *ctx) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; + const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; + struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; + const struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; + u32 dpb_longterm = 0; + u32 dpb_valid = 0; + int i; + + for (i = 0; i < HANTRO_H264_DPB_SIZE; ++i) { + tbl->poc[i * 2] = dpb[i].top_field_order_cnt; + tbl->poc[i * 2 + 1] = dpb[i].bottom_field_order_cnt; + + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) + continue; + + /* + * Set up bit maps of valid and long term DPBs. + * NOTE: The bits are reversed, i.e. MSb is DPB 0. For frame + * decoding, bit 31 to 15 are used, while for field decoding, + * all bits are used, with bit 31 being a top field, 30 a bottom + * field and so on. + */ + if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) { + if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) + dpb_valid |= REF_BIT(i * 2); + + if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) + dpb_valid |= REF_BIT(i * 2 + 1); + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) { + dpb_longterm |= REF_BIT(i * 2); + dpb_longterm |= REF_BIT(i * 2 + 1); + } + } else { + dpb_valid |= REF_BIT(i); + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + dpb_longterm |= REF_BIT(i); + } + } + ctx->h264_dec.dpb_valid = dpb_valid; + ctx->h264_dec.dpb_longterm = dpb_longterm; + + if ((dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) || + !(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)) { + tbl->poc[32] = ctx->h264_dec.cur_poc; + tbl->poc[33] = 0; + } else { + tbl->poc[32] = dec_param->top_field_order_cnt; + tbl->poc[33] = dec_param->bottom_field_order_cnt; + } + + assemble_scaling_list(ctx); +} + +static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, + const struct v4l2_h264_dpb_entry *b) +{ + return a->reference_ts == b->reference_ts; +} + +static void update_dpb(struct hantro_ctx *ctx) +{ + const struct v4l2_ctrl_h264_decode_params *dec_param; + DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + DECLARE_BITMAP(used, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + unsigned int i, j; + + dec_param = ctx->h264_dec.ctrls.decode; + + /* Disable all entries by default. */ + for (i = 0; i < ARRAY_SIZE(ctx->h264_dec.dpb); i++) + ctx->h264_dec.dpb[i].flags = 0; + + /* Try to match new DPB entries with existing ones by their POCs. */ + for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { + const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; + + if (!(ndpb->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) + continue; + + /* + * To cut off some comparisons, iterate only on target DPB + * entries which are not used yet. + */ + for_each_clear_bit(j, used, ARRAY_SIZE(ctx->h264_dec.dpb)) { + struct v4l2_h264_dpb_entry *cdpb; + + cdpb = &ctx->h264_dec.dpb[j]; + if (!dpb_entry_match(cdpb, ndpb)) + continue; + + *cdpb = *ndpb; + set_bit(j, used); + break; + } + + if (j == ARRAY_SIZE(ctx->h264_dec.dpb)) + set_bit(i, new); + } + + /* For entries that could not be matched, use remaining free slots. */ + for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { + const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; + struct v4l2_h264_dpb_entry *cdpb; + + /* + * Both arrays are of the same sizes, so there is no way + * we can end up with no space in target array, unless + * something is buggy. + */ + j = find_first_zero_bit(used, ARRAY_SIZE(ctx->h264_dec.dpb)); + if (WARN_ON(j >= ARRAY_SIZE(ctx->h264_dec.dpb))) + return; + + cdpb = &ctx->h264_dec.dpb[j]; + *cdpb = *ndpb; + set_bit(j, used); + } +} + +dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, + unsigned int dpb_idx) +{ + struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; + dma_addr_t dma_addr = 0; + s32 cur_poc = ctx->h264_dec.cur_poc; + u32 flags; + + if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) + dma_addr = hantro_get_ref(ctx, dpb[dpb_idx].reference_ts); + + if (!dma_addr) { + struct vb2_v4l2_buffer *dst_buf; + struct vb2_buffer *buf; + + /* + * If a DPB entry is unused or invalid, address of current + * destination buffer is returned. + */ + dst_buf = hantro_get_dst_buf(ctx); + buf = &dst_buf->vb2_buf; + dma_addr = hantro_get_dec_buf_addr(ctx, buf); + } + + flags = dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD ? 0x2 : 0; + flags |= abs(dpb[dpb_idx].top_field_order_cnt - cur_poc) < + abs(dpb[dpb_idx].bottom_field_order_cnt - cur_poc) ? + 0x1 : 0; + + return dma_addr | flags; +} + +u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, unsigned int dpb_idx) +{ + const struct v4l2_h264_dpb_entry *dpb = &ctx->h264_dec.dpb[dpb_idx]; + + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + return 0; + return dpb->frame_num; +} + +/* + * Removes all references with the same parity as the current picture from the + * reference list. The remaining list will have references with the opposite + * parity. This is effectively a deduplication of references since each buffer + * stores two fields. For this reason, each buffer is found twice in the + * reference list. + * + * This technique has been chosen through trial and error. This simple approach + * resulted in the highest conformance score. Note that this method may suffer + * worse quality in the case an opposite reference frame has been lost. If this + * becomes a problem in the future, it should be possible to add a preprocessing + * to identify un-paired fields and avoid removing them. + */ +static void deduplicate_reflist(struct v4l2_h264_reflist_builder *b, + struct v4l2_h264_reference *reflist) +{ + int write_idx = 0; + int i; + + if (b->cur_pic_fields == V4L2_H264_FRAME_REF) { + write_idx = b->num_valid; + goto done; + } + + for (i = 0; i < b->num_valid; i++) { + if (!(b->cur_pic_fields == reflist[i].fields)) { + reflist[write_idx++] = reflist[i]; + continue; + } + } + +done: + /* Should not happen unless we have a bug in the reflist builder. */ + if (WARN_ON(write_idx > 16)) + write_idx = 16; + + /* Clear the remaining, some streams fails otherwise */ + for (; write_idx < 16; write_idx++) + reflist[write_idx].index = 15; +} + +int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx) +{ + struct hantro_h264_dec_hw_ctx *h264_ctx = &ctx->h264_dec; + struct hantro_h264_dec_ctrls *ctrls = &h264_ctx->ctrls; + struct v4l2_h264_reflist_builder reflist_builder; + + hantro_start_prepare_run(ctx); + + ctrls->scaling = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); + if (WARN_ON(!ctrls->scaling)) + return -EINVAL; + + ctrls->decode = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); + if (WARN_ON(!ctrls->decode)) + return -EINVAL; + + ctrls->sps = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SPS); + if (WARN_ON(!ctrls->sps)) + return -EINVAL; + + ctrls->pps = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_PPS); + if (WARN_ON(!ctrls->pps)) + return -EINVAL; + + /* Update the DPB with new refs. */ + update_dpb(ctx); + + /* Build the P/B{0,1} ref lists. */ + v4l2_h264_init_reflist_builder(&reflist_builder, ctrls->decode, + ctrls->sps, ctx->h264_dec.dpb); + h264_ctx->cur_poc = reflist_builder.cur_pic_order_count; + + /* Prepare data in memory. */ + prepare_table(ctx); + + v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); + v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, + h264_ctx->reflists.b1); + + /* + * Reduce ref lists to at most 16 entries, Hantro hardware will deduce + * the actual picture lists in field through the dpb_valid, + * dpb_longterm bitmap along with the current frame parity. + */ + if (reflist_builder.cur_pic_fields != V4L2_H264_FRAME_REF) { + deduplicate_reflist(&reflist_builder, h264_ctx->reflists.p); + deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b0); + deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b1); + } + + return 0; +} + +void hantro_h264_dec_exit(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; + struct hantro_aux_buf *priv = &h264_dec->priv; + + dma_free_coherent(vpu->dev, priv->size, priv->cpu, priv->dma); +} + +int hantro_h264_dec_init(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; + struct hantro_aux_buf *priv = &h264_dec->priv; + struct hantro_h264_dec_priv_tbl *tbl; + + priv->cpu = dma_alloc_coherent(vpu->dev, sizeof(*tbl), &priv->dma, + GFP_KERNEL); + if (!priv->cpu) + return -ENOMEM; + + priv->size = sizeof(*tbl); + tbl = priv->cpu; + memcpy(tbl->cabac_table, h264_cabac_table, sizeof(tbl->cabac_table)); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_hevc.c b/drivers/media/platform/verisilicon/hantro_hevc.c new file mode 100644 index 000000000000..b990bc98164c --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_hevc.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU HEVC codec driver + * + * Copyright (C) 2020 Safran Passenger Innovations LLC + */ + +#include +#include + +#include "hantro.h" +#include "hantro_hw.h" + +#define VERT_FILTER_RAM_SIZE 8 /* bytes per pixel row */ +/* + * BSD control data of current picture at tile border + * 128 bits per 4x4 tile = 128/(8*4) bytes per row + */ +#define BSD_CTRL_RAM_SIZE 4 /* bytes per pixel row */ +/* tile border coefficients of filter */ +#define VERT_SAO_RAM_SIZE 48 /* bytes per pixel */ + +#define SCALING_LIST_SIZE (16 * 64) + +#define MAX_TILE_COLS 20 +#define MAX_TILE_ROWS 22 + +void hantro_hevc_ref_init(struct hantro_ctx *ctx) +{ + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + + hevc_dec->ref_bufs_used = 0; +} + +dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, + s32 poc) +{ + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + int i; + + /* Find the reference buffer in already known ones */ + for (i = 0; i < NUM_REF_PICTURES; i++) { + if (hevc_dec->ref_bufs_poc[i] == poc) { + hevc_dec->ref_bufs_used |= 1 << i; + return hevc_dec->ref_bufs[i].dma; + } + } + + return 0; +} + +int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr) +{ + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + int i; + + /* Add a new reference buffer */ + for (i = 0; i < NUM_REF_PICTURES; i++) { + if (!(hevc_dec->ref_bufs_used & 1 << i)) { + hevc_dec->ref_bufs_used |= 1 << i; + hevc_dec->ref_bufs_poc[i] = poc; + hevc_dec->ref_bufs[i].dma = addr; + return 0; + } + } + + return -EINVAL; +} + +static int tile_buffer_reallocate(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; + unsigned int height64 = (sps->pic_height_in_luma_samples + 63) & ~63; + unsigned int size; + + if (num_tile_cols <= 1 || + num_tile_cols <= hevc_dec->num_tile_cols_allocated) + return 0; + + /* Need to reallocate due to tiles passed via PPS */ + if (hevc_dec->tile_filter.cpu) { + dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, + hevc_dec->tile_filter.cpu, + hevc_dec->tile_filter.dma); + hevc_dec->tile_filter.cpu = NULL; + } + + if (hevc_dec->tile_sao.cpu) { + dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, + hevc_dec->tile_sao.cpu, + hevc_dec->tile_sao.dma); + hevc_dec->tile_sao.cpu = NULL; + } + + if (hevc_dec->tile_bsd.cpu) { + dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, + hevc_dec->tile_bsd.cpu, + hevc_dec->tile_bsd.dma); + hevc_dec->tile_bsd.cpu = NULL; + } + + size = VERT_FILTER_RAM_SIZE * height64 * (num_tile_cols - 1); + hevc_dec->tile_filter.cpu = dma_alloc_coherent(vpu->dev, size, + &hevc_dec->tile_filter.dma, + GFP_KERNEL); + if (!hevc_dec->tile_filter.cpu) + goto err_free_tile_buffers; + hevc_dec->tile_filter.size = size; + + size = VERT_SAO_RAM_SIZE * height64 * (num_tile_cols - 1); + hevc_dec->tile_sao.cpu = dma_alloc_coherent(vpu->dev, size, + &hevc_dec->tile_sao.dma, + GFP_KERNEL); + if (!hevc_dec->tile_sao.cpu) + goto err_free_tile_buffers; + hevc_dec->tile_sao.size = size; + + size = BSD_CTRL_RAM_SIZE * height64 * (num_tile_cols - 1); + hevc_dec->tile_bsd.cpu = dma_alloc_coherent(vpu->dev, size, + &hevc_dec->tile_bsd.dma, + GFP_KERNEL); + if (!hevc_dec->tile_bsd.cpu) + goto err_free_tile_buffers; + hevc_dec->tile_bsd.size = size; + + hevc_dec->num_tile_cols_allocated = num_tile_cols; + + return 0; + +err_free_tile_buffers: + if (hevc_dec->tile_filter.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, + hevc_dec->tile_filter.cpu, + hevc_dec->tile_filter.dma); + hevc_dec->tile_filter.cpu = NULL; + + if (hevc_dec->tile_sao.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, + hevc_dec->tile_sao.cpu, + hevc_dec->tile_sao.dma); + hevc_dec->tile_sao.cpu = NULL; + + if (hevc_dec->tile_bsd.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, + hevc_dec->tile_bsd.cpu, + hevc_dec->tile_bsd.dma); + hevc_dec->tile_bsd.cpu = NULL; + + return -ENOMEM; +} + +static int hantro_hevc_validate_sps(struct hantro_ctx *ctx, const struct v4l2_ctrl_hevc_sps *sps) +{ + /* + * for tile pixel format check if the width and height match + * hardware constraints + */ + if (ctx->vpu_dst_fmt->fourcc == V4L2_PIX_FMT_NV12_4L4) { + if (ctx->dst_fmt.width != + ALIGN(sps->pic_width_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_width)) + return -EINVAL; + + if (ctx->dst_fmt.height != + ALIGN(sps->pic_height_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_height)) + return -EINVAL; + } + + return 0; +} + +int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx) +{ + struct hantro_hevc_dec_hw_ctx *hevc_ctx = &ctx->hevc_dec; + struct hantro_hevc_dec_ctrls *ctrls = &hevc_ctx->ctrls; + int ret; + + hantro_start_prepare_run(ctx); + + ctrls->decode_params = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); + if (WARN_ON(!ctrls->decode_params)) + return -EINVAL; + + ctrls->scaling = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); + if (WARN_ON(!ctrls->scaling)) + return -EINVAL; + + ctrls->sps = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SPS); + if (WARN_ON(!ctrls->sps)) + return -EINVAL; + + ret = hantro_hevc_validate_sps(ctx, ctrls->sps); + if (ret) + return ret; + + ctrls->pps = + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_PPS); + if (WARN_ON(!ctrls->pps)) + return -EINVAL; + + ret = tile_buffer_reallocate(ctx); + if (ret) + return ret; + + return 0; +} + +void hantro_hevc_dec_exit(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + + if (hevc_dec->tile_sizes.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_sizes.size, + hevc_dec->tile_sizes.cpu, + hevc_dec->tile_sizes.dma); + hevc_dec->tile_sizes.cpu = NULL; + + if (hevc_dec->scaling_lists.cpu) + dma_free_coherent(vpu->dev, hevc_dec->scaling_lists.size, + hevc_dec->scaling_lists.cpu, + hevc_dec->scaling_lists.dma); + hevc_dec->scaling_lists.cpu = NULL; + + if (hevc_dec->tile_filter.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, + hevc_dec->tile_filter.cpu, + hevc_dec->tile_filter.dma); + hevc_dec->tile_filter.cpu = NULL; + + if (hevc_dec->tile_sao.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, + hevc_dec->tile_sao.cpu, + hevc_dec->tile_sao.dma); + hevc_dec->tile_sao.cpu = NULL; + + if (hevc_dec->tile_bsd.cpu) + dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, + hevc_dec->tile_bsd.cpu, + hevc_dec->tile_bsd.dma); + hevc_dec->tile_bsd.cpu = NULL; +} + +int hantro_hevc_dec_init(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; + unsigned int size; + + memset(hevc_dec, 0, sizeof(*hevc_dec)); + + /* + * Maximum number of tiles times width and height (2 bytes each), + * rounding up to next 16 bytes boundary + one extra 16 byte + * chunk (HW guys wanted to have this). + */ + size = round_up(MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 16, 16); + hevc_dec->tile_sizes.cpu = dma_alloc_coherent(vpu->dev, size, + &hevc_dec->tile_sizes.dma, + GFP_KERNEL); + if (!hevc_dec->tile_sizes.cpu) + return -ENOMEM; + + hevc_dec->tile_sizes.size = size; + + hevc_dec->scaling_lists.cpu = dma_alloc_coherent(vpu->dev, SCALING_LIST_SIZE, + &hevc_dec->scaling_lists.dma, + GFP_KERNEL); + if (!hevc_dec->scaling_lists.cpu) + return -ENOMEM; + + hevc_dec->scaling_lists.size = SCALING_LIST_SIZE; + + hantro_hevc_ref_init(ctx); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h new file mode 100644 index 000000000000..e83f0c523a30 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright 2018 Google LLC. + * Tomasz Figa + */ + +#ifndef HANTRO_HW_H_ +#define HANTRO_HW_H_ + +#include +#include +#include +#include +#include + +#define DEC_8190_ALIGN_MASK 0x07U + +#define MB_DIM 16 +#define TILE_MB_DIM 4 +#define MB_WIDTH(w) DIV_ROUND_UP(w, MB_DIM) +#define MB_HEIGHT(h) DIV_ROUND_UP(h, MB_DIM) + +#define FMT_MIN_WIDTH 48 +#define FMT_MIN_HEIGHT 48 +#define FMT_HD_WIDTH 1280 +#define FMT_HD_HEIGHT 720 +#define FMT_FHD_WIDTH 1920 +#define FMT_FHD_HEIGHT 1088 +#define FMT_UHD_WIDTH 3840 +#define FMT_UHD_HEIGHT 2160 +#define FMT_4K_WIDTH 4096 +#define FMT_4K_HEIGHT 2304 + +#define NUM_REF_PICTURES (V4L2_HEVC_DPB_ENTRIES_NUM_MAX + 1) + +struct hantro_dev; +struct hantro_ctx; +struct hantro_buf; +struct hantro_variant; + +/** + * struct hantro_aux_buf - auxiliary DMA buffer for hardware data + * + * @cpu: CPU pointer to the buffer. + * @dma: DMA address of the buffer. + * @size: Size of the buffer. + * @attrs: Attributes of the DMA mapping. + */ +struct hantro_aux_buf { + void *cpu; + dma_addr_t dma; + size_t size; + unsigned long attrs; +}; + +/* Max. number of entries in the DPB (HW limitation). */ +#define HANTRO_H264_DPB_SIZE 16 + +/** + * struct hantro_h264_dec_ctrls + * + * @decode: Decode params + * @scaling: Scaling info + * @sps: SPS info + * @pps: PPS info + */ +struct hantro_h264_dec_ctrls { + const struct v4l2_ctrl_h264_decode_params *decode; + const struct v4l2_ctrl_h264_scaling_matrix *scaling; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; +}; + +/** + * struct hantro_h264_dec_reflists + * + * @p: P reflist + * @b0: B0 reflist + * @b1: B1 reflist + */ +struct hantro_h264_dec_reflists { + struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; +}; + +/** + * struct hantro_h264_dec_hw_ctx + * + * @priv: Private auxiliary buffer for hardware. + * @dpb: DPB + * @reflists: P/B0/B1 reflists + * @ctrls: V4L2 controls attached to a run + * @dpb_longterm: DPB long-term + * @dpb_valid: DPB valid + * @cur_poc: Current picture order count + */ +struct hantro_h264_dec_hw_ctx { + struct hantro_aux_buf priv; + struct v4l2_h264_dpb_entry dpb[HANTRO_H264_DPB_SIZE]; + struct hantro_h264_dec_reflists reflists; + struct hantro_h264_dec_ctrls ctrls; + u32 dpb_longterm; + u32 dpb_valid; + s32 cur_poc; +}; + +/** + * struct hantro_hevc_dec_ctrls + * @decode_params: Decode params + * @scaling: Scaling matrix + * @sps: SPS info + * @pps: PPS info + * @hevc_hdr_skip_length: the number of data (in bits) to skip in the + * slice segment header syntax after 'slice type' + * token + */ +struct hantro_hevc_dec_ctrls { + const struct v4l2_ctrl_hevc_decode_params *decode_params; + const struct v4l2_ctrl_hevc_scaling_matrix *scaling; + const struct v4l2_ctrl_hevc_sps *sps; + const struct v4l2_ctrl_hevc_pps *pps; + u32 hevc_hdr_skip_length; +}; + +/** + * struct hantro_hevc_dec_hw_ctx + * @tile_sizes: Tile sizes buffer + * @tile_filter: Tile vertical filter buffer + * @tile_sao: Tile SAO buffer + * @tile_bsd: Tile BSD control buffer + * @ref_bufs: Internal reference buffers + * @scaling_lists: Scaling lists buffer + * @ref_bufs_poc: Internal reference buffers picture order count + * @ref_bufs_used: Bitfield of used reference buffers + * @ctrls: V4L2 controls attached to a run + * @num_tile_cols_allocated: number of allocated tiles + */ +struct hantro_hevc_dec_hw_ctx { + struct hantro_aux_buf tile_sizes; + struct hantro_aux_buf tile_filter; + struct hantro_aux_buf tile_sao; + struct hantro_aux_buf tile_bsd; + struct hantro_aux_buf ref_bufs[NUM_REF_PICTURES]; + struct hantro_aux_buf scaling_lists; + s32 ref_bufs_poc[NUM_REF_PICTURES]; + u32 ref_bufs_used; + struct hantro_hevc_dec_ctrls ctrls; + unsigned int num_tile_cols_allocated; +}; + +/** + * struct hantro_mpeg2_dec_hw_ctx + * + * @qtable: Quantization table + */ +struct hantro_mpeg2_dec_hw_ctx { + struct hantro_aux_buf qtable; +}; + +/** + * struct hantro_vp8_dec_hw_ctx + * + * @segment_map: Segment map buffer. + * @prob_tbl: Probability table buffer. + */ +struct hantro_vp8_dec_hw_ctx { + struct hantro_aux_buf segment_map; + struct hantro_aux_buf prob_tbl; +}; + +/** + * struct hantro_vp9_frame_info + * + * @valid: frame info valid flag + * @frame_context_idx: index of frame context + * @reference_mode: inter prediction type + * @tx_mode: transform mode + * @interpolation_filter: filter selection for inter prediction + * @flags: frame flags + * @timestamp: frame timestamp + */ +struct hantro_vp9_frame_info { + u32 valid : 1; + u32 frame_context_idx : 2; + u32 reference_mode : 2; + u32 tx_mode : 3; + u32 interpolation_filter : 3; + u32 flags; + u64 timestamp; +}; + +#define MAX_SB_COLS 64 +#define MAX_SB_ROWS 34 + +/** + * struct hantro_vp9_dec_hw_ctx + * + * @tile_edge: auxiliary DMA buffer for tile edge processing + * @segment_map: auxiliary DMA buffer for segment map + * @misc: auxiliary DMA buffer for tile info, probabilities and hw counters + * @cnts: vp9 library struct for abstracting hw counters access + * @probability_tables: VP9 probability tables implied by the spec + * @frame_context: VP9 frame contexts + * @cur: current frame information + * @last: last frame information + * @bsd_ctrl_offset: bsd offset into tile_edge + * @segment_map_size: size of segment map + * @ctx_counters_offset: hw counters offset into misc + * @tile_info_offset: tile info offset into misc + * @tile_r_info: per-tile information array + * @tile_c_info: per-tile information array + * @last_tile_r: last number of tile rows + * @last_tile_c: last number of tile cols + * @last_sbs_r: last number of superblock rows + * @last_sbs_c: last number of superblock cols + * @active_segment: number of active segment (alternating between 0 and 1) + * @feature_enabled: segmentation feature enabled flags + * @feature_data: segmentation feature data + */ +struct hantro_vp9_dec_hw_ctx { + struct hantro_aux_buf tile_edge; + struct hantro_aux_buf segment_map; + struct hantro_aux_buf misc; + struct v4l2_vp9_frame_symbol_counts cnts; + struct v4l2_vp9_frame_context probability_tables; + struct v4l2_vp9_frame_context frame_context[4]; + struct hantro_vp9_frame_info cur; + struct hantro_vp9_frame_info last; + + unsigned int bsd_ctrl_offset; + unsigned int segment_map_size; + unsigned int ctx_counters_offset; + unsigned int tile_info_offset; + + unsigned short tile_r_info[MAX_SB_ROWS]; + unsigned short tile_c_info[MAX_SB_COLS]; + unsigned int last_tile_r; + unsigned int last_tile_c; + unsigned int last_sbs_r; + unsigned int last_sbs_c; + + unsigned int active_segment; + u8 feature_enabled[8]; + s16 feature_data[8][4]; +}; + +/** + * struct hantro_postproc_ctx + * + * @dec_q: References buffers, in decoder format. + */ +struct hantro_postproc_ctx { + struct hantro_aux_buf dec_q[VB2_MAX_FRAME]; +}; + +/** + * struct hantro_postproc_ops - post-processor operations + * + * @enable: Enable the post-processor block. Optional. + * @disable: Disable the post-processor block. Optional. + * @enum_framesizes: Enumerate possible scaled output formats. + * Returns zero if OK, a negative value in error cases. + * Optional. + */ +struct hantro_postproc_ops { + void (*enable)(struct hantro_ctx *ctx); + void (*disable)(struct hantro_ctx *ctx); + int (*enum_framesizes)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize); +}; + +/** + * struct hantro_codec_ops - codec mode specific operations + * + * @init: If needed, can be used for initialization. + * Optional and called from process context. + * @exit: If needed, can be used to undo the .init phase. + * Optional and called from process context. + * @run: Start single {en,de)coding job. Called from atomic context + * to indicate that a pair of buffers is ready and the hardware + * should be programmed and started. Returns zero if OK, a + * negative value in error cases. + * @done: Read back processing results and additional data from hardware. + * @reset: Reset the hardware in case of a timeout. + */ +struct hantro_codec_ops { + int (*init)(struct hantro_ctx *ctx); + void (*exit)(struct hantro_ctx *ctx); + int (*run)(struct hantro_ctx *ctx); + void (*done)(struct hantro_ctx *ctx); + void (*reset)(struct hantro_ctx *ctx); +}; + +/** + * enum hantro_enc_fmt - source format ID for hardware registers. + * + * @ROCKCHIP_VPU_ENC_FMT_YUV420P: Y/CbCr 4:2:0 planar format + * @ROCKCHIP_VPU_ENC_FMT_YUV420SP: Y/CbCr 4:2:0 semi-planar format + * @ROCKCHIP_VPU_ENC_FMT_YUYV422: YUV 4:2:2 packed format (YUYV) + * @ROCKCHIP_VPU_ENC_FMT_UYVY422: YUV 4:2:2 packed format (UYVY) + */ +enum hantro_enc_fmt { + ROCKCHIP_VPU_ENC_FMT_YUV420P = 0, + ROCKCHIP_VPU_ENC_FMT_YUV420SP = 1, + ROCKCHIP_VPU_ENC_FMT_YUYV422 = 2, + ROCKCHIP_VPU_ENC_FMT_UYVY422 = 3, +}; + +extern const struct hantro_variant imx8mm_vpu_g1_variant; +extern const struct hantro_variant imx8mq_vpu_g1_variant; +extern const struct hantro_variant imx8mq_vpu_g2_variant; +extern const struct hantro_variant imx8mq_vpu_variant; +extern const struct hantro_variant px30_vpu_variant; +extern const struct hantro_variant rk3036_vpu_variant; +extern const struct hantro_variant rk3066_vpu_variant; +extern const struct hantro_variant rk3288_vpu_variant; +extern const struct hantro_variant rk3328_vpu_variant; +extern const struct hantro_variant rk3399_vpu_variant; +extern const struct hantro_variant rk3568_vepu_variant; +extern const struct hantro_variant rk3568_vpu_variant; +extern const struct hantro_variant sama5d4_vdec_variant; +extern const struct hantro_variant sunxi_vpu_variant; + +extern const struct hantro_postproc_ops hantro_g1_postproc_ops; +extern const struct hantro_postproc_ops hantro_g2_postproc_ops; + +extern const u32 hantro_vp8_dec_mc_filter[8][6]; + +void hantro_watchdog(struct work_struct *work); +void hantro_run(struct hantro_ctx *ctx); +void hantro_irq_done(struct hantro_dev *vpu, + enum vb2_buffer_state result); +void hantro_start_prepare_run(struct hantro_ctx *ctx); +void hantro_end_prepare_run(struct hantro_ctx *ctx); + +irqreturn_t hantro_g1_irq(int irq, void *dev_id); +void hantro_g1_reset(struct hantro_ctx *ctx); + +int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); +int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx); +void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx); +void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx); + +dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, + unsigned int dpb_idx); +u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, + unsigned int dpb_idx); +int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx); +int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx); +int hantro_g1_h264_dec_run(struct hantro_ctx *ctx); +int hantro_h264_dec_init(struct hantro_ctx *ctx); +void hantro_h264_dec_exit(struct hantro_ctx *ctx); + +int hantro_hevc_dec_init(struct hantro_ctx *ctx); +void hantro_hevc_dec_exit(struct hantro_ctx *ctx); +int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx); +int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx); +void hantro_hevc_ref_init(struct hantro_ctx *ctx); +dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, s32 poc); +int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr); + + +static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension) +{ + return (dimension + 63) / 64; +} + +static inline size_t +hantro_vp9_mv_size(unsigned int width, unsigned int height) +{ + int num_ctbs; + + /* + * There can be up to (CTBs x 64) number of blocks, + * and the motion vector for each block needs 16 bytes. + */ + num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height); + return (num_ctbs * 64) * 16; +} + +static inline size_t +hantro_h264_mv_size(unsigned int width, unsigned int height) +{ + /* + * A decoded 8-bit 4:2:0 NV12 frame may need memory for up to + * 448 bytes per macroblock with additional 32 bytes on + * multi-core variants. + * + * The H264 decoder needs extra space on the output buffers + * to store motion vectors. This is needed for reference + * frames and only if the format is non-post-processed NV12. + * + * Memory layout is as follow: + * + * +---------------------------+ + * | Y-plane 256 bytes x MBs | + * +---------------------------+ + * | UV-plane 128 bytes x MBs | + * +---------------------------+ + * | MV buffer 64 bytes x MBs | + * +---------------------------+ + * | MC sync 32 bytes | + * +---------------------------+ + */ + return 64 * MB_WIDTH(width) * MB_WIDTH(height) + 32; +} + +static inline size_t +hantro_hevc_mv_size(unsigned int width, unsigned int height) +{ + /* + * A CTB can be 64x64, 32x32 or 16x16. + * Allocated memory for the "worse" case: 16x16 + */ + return width * height / 16; +} + +int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); +int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); +void hantro_mpeg2_dec_copy_qtable(u8 *qtable, + const struct v4l2_ctrl_mpeg2_quantisation *ctrl); +int hantro_mpeg2_dec_init(struct hantro_ctx *ctx); +void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx); + +int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx); +int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx); +int hantro_vp8_dec_init(struct hantro_ctx *ctx); +void hantro_vp8_dec_exit(struct hantro_ctx *ctx); +void hantro_vp8_prob_update(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr); + +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx); +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx); +int hantro_vp9_dec_init(struct hantro_ctx *ctx); +void hantro_vp9_dec_exit(struct hantro_ctx *ctx); +void hantro_g2_check_idle(struct hantro_dev *vpu); +irqreturn_t hantro_g2_irq(int irq, void *dev_id); + +#endif /* HANTRO_HW_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.c b/drivers/media/platform/verisilicon/hantro_jpeg.c new file mode 100644 index 000000000000..d07b1b449b61 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_jpeg.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Collabora, Ltd. + * + * Based on GSPCA and CODA drivers: + * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2014 Philipp Zabel, Pengutronix + */ + +#include +#include +#include +#include +#include "hantro_jpeg.h" +#include "hantro.h" + +#define LUMA_QUANT_OFF 25 +#define CHROMA_QUANT_OFF 90 +#define HEIGHT_OFF 159 +#define WIDTH_OFF 161 + +#define HUFF_LUMA_DC_OFF 178 +#define HUFF_LUMA_AC_OFF 211 +#define HUFF_CHROMA_DC_OFF 394 +#define HUFF_CHROMA_AC_OFF 427 + +/* Default tables from JPEG ITU-T.81 + * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 + */ +static const unsigned char luma_q_table[] = { + 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, + 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, + 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, + 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, + 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, + 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, + 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, + 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 +}; + +static const unsigned char chroma_q_table[] = { + 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, + 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, + 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 +}; + +static const unsigned char zigzag[] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const u32 hw_reorder[] = { + 0, 8, 16, 24, 1, 9, 17, 25, + 32, 40, 48, 56, 33, 41, 49, 57, + 2, 10, 18, 26, 3, 11, 19, 27, + 34, 42, 50, 58, 35, 43, 51, 59, + 4, 12, 20, 28, 5, 13, 21, 29, + 36, 44, 52, 60, 37, 45, 53, 61, + 6, 14, 22, 30, 7, 15, 23, 31, + 38, 46, 54, 62, 39, 47, 55, 63 +}; + +/* Huffman tables are shared with CODA */ +static const unsigned char luma_dc_table[] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char chroma_dc_table[] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, +}; + +static const unsigned char luma_ac_table[] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +static const unsigned char chroma_ac_table[] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +/* For simplicity, we keep a pre-formatted JPEG header, + * and we'll use fixed offsets to change the width, height + * quantization tables, etc. + */ +static const unsigned char hantro_jpeg_header[] = { + /* SOI */ + 0xff, 0xd8, + + /* JFIF-APP0 */ + 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, + + /* DQT */ + 0xff, 0xdb, 0x00, 0x84, + + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* SOF */ + 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, + 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, + 0x03, 0x11, 0x01, + + /* DHT */ + 0xff, 0xc4, 0x00, 0x1f, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + /* DHT */ + 0xff, 0xc4, 0x00, 0xb5, 0x10, + + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* DHT */ + 0xff, 0xc4, 0x00, 0x1f, 0x01, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + /* DHT */ + 0xff, 0xc4, 0x00, 0xb5, 0x11, + + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* COM */ + 0xff, 0xfe, 0x00, 0x03, 0x00, + + /* SOS */ + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, + 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, +}; + +/* + * JPEG_HEADER_SIZE is used in other parts of the driver in lieu of + * "sizeof(hantro_jpeg_header)". The two must be equal. + */ +static_assert(sizeof(hantro_jpeg_header) == JPEG_HEADER_SIZE); + +/* + * hantro_jpeg_header is padded with a COM segment, so that the payload + * of the SOS segment (the entropy-encoded image scan), which should + * trail the whole header, is 8-byte aligned for the hardware to write + * to directly. + */ +static_assert(IS_ALIGNED(sizeof(hantro_jpeg_header), 8), + "Hantro JPEG header size needs to be 8-byte aligned."); + +static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) +{ + unsigned int temp; + + temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + + return (unsigned char)temp; +} + +static void +jpeg_scale_quant_table(unsigned char *file_q_tab, + unsigned char *reordered_q_tab, + const unsigned char *tab, int scale) +{ + int i; + + BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); + + for (i = 0; i < JPEG_QUANT_SIZE; i++) { + file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); + } +} + +static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) +{ + int scale; + + /* + * Non-linear scaling factor: + * [5,50] -> [1000..100], [51,100] -> [98..0] + */ + if (ctx->quality < 50) + scale = 5000 / ctx->quality; + else + scale = 200 - 2 * ctx->quality; + + BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); + + jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, + ctx->hw_luma_qtable, luma_q_table, scale); + jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, + ctx->hw_chroma_qtable, chroma_q_table, scale); +} + +void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) +{ + char *buf = ctx->buffer; + + memcpy(buf, hantro_jpeg_header, + sizeof(hantro_jpeg_header)); + + buf[HEIGHT_OFF + 0] = ctx->height >> 8; + buf[HEIGHT_OFF + 1] = ctx->height; + buf[WIDTH_OFF + 0] = ctx->width >> 8; + buf[WIDTH_OFF + 1] = ctx->width; + + memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); + memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); + memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, + sizeof(chroma_dc_table)); + memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, + sizeof(chroma_ac_table)); + + jpeg_set_quality(ctx); +} diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.h b/drivers/media/platform/verisilicon/hantro_jpeg.h new file mode 100644 index 000000000000..0b49d0b82caa --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_jpeg.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#define JPEG_HEADER_SIZE 624 +#define JPEG_QUANT_SIZE 64 + +struct hantro_jpeg_ctx { + int width; + int height; + int quality; + unsigned char *buffer; + unsigned char hw_luma_qtable[JPEG_QUANT_SIZE]; + unsigned char hw_chroma_qtable[JPEG_QUANT_SIZE]; +}; + +void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/media/platform/verisilicon/hantro_mpeg2.c b/drivers/media/platform/verisilicon/hantro_mpeg2.c new file mode 100644 index 000000000000..04e545eb0a83 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_mpeg2.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include "hantro.h" + +static const u8 zigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +void hantro_mpeg2_dec_copy_qtable(u8 *qtable, + const struct v4l2_ctrl_mpeg2_quantisation *ctrl) +{ + int i, n; + + if (!qtable || !ctrl) + return; + + for (i = 0; i < ARRAY_SIZE(zigzag); i++) { + n = zigzag[i]; + qtable[n + 0] = ctrl->intra_quantiser_matrix[i]; + qtable[n + 64] = ctrl->non_intra_quantiser_matrix[i]; + qtable[n + 128] = ctrl->chroma_intra_quantiser_matrix[i]; + qtable[n + 192] = ctrl->chroma_non_intra_quantiser_matrix[i]; + } +} + +int hantro_mpeg2_dec_init(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + ctx->mpeg2_dec.qtable.size = ARRAY_SIZE(zigzag) * 4; + ctx->mpeg2_dec.qtable.cpu = + dma_alloc_coherent(vpu->dev, + ctx->mpeg2_dec.qtable.size, + &ctx->mpeg2_dec.qtable.dma, + GFP_KERNEL); + if (!ctx->mpeg2_dec.qtable.cpu) + return -ENOMEM; + return 0; +} + +void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + dma_free_coherent(vpu->dev, + ctx->mpeg2_dec.qtable.size, + ctx->mpeg2_dec.qtable.cpu, + ctx->mpeg2_dec.qtable.dma); +} diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c new file mode 100644 index 000000000000..a0928c508434 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro G1 post-processor support + * + * Copyright (C) 2019 Collabora, Ltd. + */ + +#include +#include + +#include "hantro.h" +#include "hantro_hw.h" +#include "hantro_g1_regs.h" +#include "hantro_g2_regs.h" +#include "hantro_v4l2.h" + +#define HANTRO_PP_REG_WRITE(vpu, reg_name, val) \ +{ \ + hantro_reg_write(vpu, \ + &hantro_g1_postproc_regs.reg_name, \ + val); \ +} + +#define HANTRO_PP_REG_WRITE_S(vpu, reg_name, val) \ +{ \ + hantro_reg_write_s(vpu, \ + &hantro_g1_postproc_regs.reg_name, \ + val); \ +} + +#define VPU_PP_IN_YUYV 0x0 +#define VPU_PP_IN_NV12 0x1 +#define VPU_PP_IN_YUV420 0x2 +#define VPU_PP_IN_YUV240_TILED 0x5 +#define VPU_PP_OUT_RGB 0x0 +#define VPU_PP_OUT_YUYV 0x3 + +static const struct hantro_postproc_regs hantro_g1_postproc_regs = { + .pipeline_en = {G1_REG_PP_INTERRUPT, 1, 0x1}, + .max_burst = {G1_REG_PP_DEV_CONFIG, 0, 0x1f}, + .clk_gate = {G1_REG_PP_DEV_CONFIG, 1, 0x1}, + .out_swap32 = {G1_REG_PP_DEV_CONFIG, 5, 0x1}, + .out_endian = {G1_REG_PP_DEV_CONFIG, 6, 0x1}, + .out_luma_base = {G1_REG_PP_OUT_LUMA_BASE, 0, 0xffffffff}, + .input_width = {G1_REG_PP_INPUT_SIZE, 0, 0x1ff}, + .input_height = {G1_REG_PP_INPUT_SIZE, 9, 0x1ff}, + .output_width = {G1_REG_PP_CONTROL, 4, 0x7ff}, + .output_height = {G1_REG_PP_CONTROL, 15, 0x7ff}, + .input_fmt = {G1_REG_PP_CONTROL, 29, 0x7}, + .output_fmt = {G1_REG_PP_CONTROL, 26, 0x7}, + .orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff}, + .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff}, +}; + +bool hantro_needs_postproc(const struct hantro_ctx *ctx, + const struct hantro_fmt *fmt) +{ + if (ctx->is_encoder) + return false; + return fmt->postprocessed; +} + +static void hantro_postproc_g1_enable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *dst_buf; + u32 src_pp_fmt, dst_pp_fmt; + dma_addr_t dst_dma; + + /* Turn on pipeline mode. Must be done first. */ + HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x1); + + src_pp_fmt = VPU_PP_IN_NV12; + + switch (ctx->vpu_dst_fmt->fourcc) { + case V4L2_PIX_FMT_YUYV: + dst_pp_fmt = VPU_PP_OUT_YUYV; + break; + default: + WARN(1, "output format %d not supported by the post-processor, this wasn't expected.", + ctx->vpu_dst_fmt->fourcc); + dst_pp_fmt = 0; + break; + } + + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + + HANTRO_PP_REG_WRITE(vpu, clk_gate, 0x1); + HANTRO_PP_REG_WRITE(vpu, out_endian, 0x1); + HANTRO_PP_REG_WRITE(vpu, out_swap32, 0x1); + HANTRO_PP_REG_WRITE(vpu, max_burst, 16); + HANTRO_PP_REG_WRITE(vpu, out_luma_base, dst_dma); + HANTRO_PP_REG_WRITE(vpu, input_width, MB_WIDTH(ctx->dst_fmt.width)); + HANTRO_PP_REG_WRITE(vpu, input_height, MB_HEIGHT(ctx->dst_fmt.height)); + HANTRO_PP_REG_WRITE(vpu, input_fmt, src_pp_fmt); + HANTRO_PP_REG_WRITE(vpu, output_fmt, dst_pp_fmt); + HANTRO_PP_REG_WRITE(vpu, output_width, ctx->dst_fmt.width); + HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height); + HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width)); + HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width); +} + +static int down_scale_factor(struct hantro_ctx *ctx) +{ + if (ctx->src_fmt.width == ctx->dst_fmt.width) + return 0; + + return DIV_ROUND_CLOSEST(ctx->src_fmt.width, ctx->dst_fmt.width); +} + +static void hantro_postproc_g2_enable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *dst_buf; + int down_scale = down_scale_factor(ctx); + size_t chroma_offset; + dma_addr_t dst_dma; + + dst_buf = hantro_get_dst_buf(ctx); + dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + chroma_offset = ctx->dst_fmt.plane_fmt[0].bytesperline * + ctx->dst_fmt.height; + + if (down_scale) { + hantro_reg_write(vpu, &g2_down_scale_e, 1); + hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2); + hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2); + hantro_write_addr(vpu, G2_DS_DST, dst_dma); + hantro_write_addr(vpu, G2_DS_DST_CHR, dst_dma + (chroma_offset >> down_scale)); + } else { + hantro_write_addr(vpu, G2_RS_OUT_LUMA_ADDR, dst_dma); + hantro_write_addr(vpu, G2_RS_OUT_CHROMA_ADDR, dst_dma + chroma_offset); + } + if (ctx->dev->variant->legacy_regs) { + int out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat); + u8 pp_shift = 0; + + if (out_depth > 8) + pp_shift = 16 - out_depth; + + hantro_reg_write(ctx->dev, &g2_rs_out_bit_depth, out_depth); + hantro_reg_write(ctx->dev, &g2_pp_pix_shift, pp_shift); + } + hantro_reg_write(vpu, &g2_out_rs_e, 1); +} + +static int hantro_postproc_g2_enum_framesizes(struct hantro_ctx *ctx, + struct v4l2_frmsizeenum *fsize) +{ + /** + * G2 scaler can scale down by 0, 2, 4 or 8 + * use fsize->index has power of 2 diviser + **/ + if (fsize->index > 3) + return -EINVAL; + + if (!ctx->src_fmt.width || !ctx->src_fmt.height) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = ctx->src_fmt.width >> fsize->index; + fsize->discrete.height = ctx->src_fmt.height >> fsize->index; + + return 0; +} + +void hantro_postproc_free(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + unsigned int i; + + for (i = 0; i < VB2_MAX_FRAME; ++i) { + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; + + if (priv->cpu) { + dma_free_attrs(vpu->dev, priv->size, priv->cpu, + priv->dma, priv->attrs); + priv->cpu = NULL; + } + } +} + +int hantro_postproc_alloc(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; + unsigned int num_buffers = cap_queue->num_buffers; + struct v4l2_pix_format_mplane pix_mp; + const struct hantro_fmt *fmt; + unsigned int i, buf_size; + + /* this should always pick native format */ + fmt = hantro_get_default_fmt(ctx, false); + if (!fmt) + return -EINVAL; + v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, + ctx->src_fmt.height); + + buf_size = pix_mp.plane_fmt[0].sizeimage; + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) + buf_size += hantro_h264_mv_size(pix_mp.width, + pix_mp.height); + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) + buf_size += hantro_vp9_mv_size(pix_mp.width, + pix_mp.height); + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) + buf_size += hantro_hevc_mv_size(pix_mp.width, + pix_mp.height); + + for (i = 0; i < num_buffers; ++i) { + struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; + + /* + * The buffers on this queue are meant as intermediate + * buffers for the decoder, so no mapping is needed. + */ + priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; + priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, + GFP_KERNEL, priv->attrs); + if (!priv->cpu) + return -ENOMEM; + priv->size = buf_size; + } + return 0; +} + +static void hantro_postproc_g1_disable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x0); +} + +static void hantro_postproc_g2_disable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + hantro_reg_write(vpu, &g2_out_rs_e, 0); +} + +void hantro_postproc_disable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->disable) + vpu->variant->postproc_ops->disable(ctx); +} + +void hantro_postproc_enable(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enable) + vpu->variant->postproc_ops->enable(ctx); +} + +int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, + struct v4l2_frmsizeenum *fsize) +{ + struct hantro_dev *vpu = ctx->dev; + + if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enum_framesizes) + return vpu->variant->postproc_ops->enum_framesizes(ctx, fsize); + + return -EINVAL; +} + +const struct hantro_postproc_ops hantro_g1_postproc_ops = { + .enable = hantro_postproc_g1_enable, + .disable = hantro_postproc_g1_disable, +}; + +const struct hantro_postproc_ops hantro_g2_postproc_ops = { + .enable = hantro_postproc_g2_enable, + .disable = hantro_postproc_g2_disable, + .enum_framesizes = hantro_postproc_g2_enum_framesizes, +}; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c new file mode 100644 index 000000000000..2c7a805289e7 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Collabora, Ltd. + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Alpha Lin + * Jeffy Chen + * + * Copyright 2018 Google LLC. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hantro.h" +#include "hantro_hw.h" +#include "hantro_v4l2.h" + +static int hantro_set_fmt_out(struct hantro_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp); +static int hantro_set_fmt_cap(struct hantro_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp); + +static const struct hantro_fmt * +hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) +{ + const struct hantro_fmt *formats; + + if (ctx->is_encoder) { + formats = ctx->dev->variant->enc_fmts; + *num_fmts = ctx->dev->variant->num_enc_fmts; + } else { + formats = ctx->dev->variant->dec_fmts; + *num_fmts = ctx->dev->variant->num_dec_fmts; + } + + return formats; +} + +static const struct hantro_fmt * +hantro_get_postproc_formats(const struct hantro_ctx *ctx, + unsigned int *num_fmts) +{ + struct hantro_dev *vpu = ctx->dev; + + if (ctx->is_encoder || !vpu->variant->postproc_fmts) { + *num_fmts = 0; + return NULL; + } + + *num_fmts = ctx->dev->variant->num_postproc_fmts; + return ctx->dev->variant->postproc_fmts; +} + +int hantro_get_format_depth(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_P010: + case V4L2_PIX_FMT_P010_4L4: + return 10; + default: + return 8; + } +} + +static bool +hantro_check_depth_match(const struct hantro_ctx *ctx, + const struct hantro_fmt *fmt) +{ + int fmt_depth, ctx_depth = 8; + + if (!fmt->match_depth && !fmt->postprocessed) + return true; + + /* 0 means default depth, which is 8 */ + if (ctx->bit_depth) + ctx_depth = ctx->bit_depth; + + fmt_depth = hantro_get_format_depth(fmt->fourcc); + + /* + * Allow only downconversion for postproc formats for now. + * It may be possible to relax that on some HW. + */ + if (!fmt->match_depth) + return fmt_depth <= ctx_depth; + + return fmt_depth == ctx_depth; +} + +static const struct hantro_fmt * +hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) +{ + const struct hantro_fmt *formats; + unsigned int i, num_fmts; + + formats = hantro_get_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) + if (formats[i].fourcc == fourcc) + return &formats[i]; + + formats = hantro_get_postproc_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) + if (formats[i].fourcc == fourcc) + return &formats[i]; + return NULL; +} + +const struct hantro_fmt * +hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream) +{ + const struct hantro_fmt *formats; + unsigned int i, num_fmts; + + formats = hantro_get_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) { + if (bitstream == (formats[i].codec_mode != + HANTRO_MODE_NONE) && + hantro_check_depth_match(ctx, &formats[i])) + return &formats[i]; + } + return NULL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hantro_dev *vpu = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, vpu->dev->driver->name, sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform: %s", + vpu->dev->driver->name); + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + const struct hantro_fmt *fmt; + + fmt = hantro_find_format(ctx, fsize->pixel_format); + if (!fmt) { + vpu_debug(0, "unsupported bitstream format (%08x)\n", + fsize->pixel_format); + return -EINVAL; + } + + /* For non-coded formats check if postprocessing scaling is possible */ + if (fmt->codec_mode == HANTRO_MODE_NONE && hantro_needs_postproc(ctx, fmt)) { + return hanto_postproc_enum_framesizes(ctx, fsize); + } else if (fsize->index != 0) { + vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", + fsize->index); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->frmsize; + + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f, bool capture) + +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + const struct hantro_fmt *fmt, *formats; + unsigned int num_fmts, i, j = 0; + bool skip_mode_none; + + /* + * When dealing with an encoder: + * - on the capture side we want to filter out all MODE_NONE formats. + * - on the output side we want to filter out all formats that are + * not MODE_NONE. + * When dealing with a decoder: + * - on the capture side we want to filter out all formats that are + * not MODE_NONE. + * - on the output side we want to filter out all MODE_NONE formats. + */ + skip_mode_none = capture == ctx->is_encoder; + + formats = hantro_get_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) { + bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; + fmt = &formats[i]; + + if (skip_mode_none == mode_none) + continue; + if (!hantro_check_depth_match(ctx, fmt)) + continue; + if (j == f->index) { + f->pixelformat = fmt->fourcc; + return 0; + } + ++j; + } + + /* + * Enumerate post-processed formats. As per the specification, + * we enumerated these formats after natively decoded formats + * as a hint for applications on what's the preferred fomat. + */ + if (!capture) + return -EINVAL; + formats = hantro_get_postproc_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) { + fmt = &formats[i]; + + if (!hantro_check_depth_match(ctx, fmt)) + continue; + if (j == f->index) { + f->pixelformat = fmt->fourcc; + return 0; + } + ++j; + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(file, priv, f, true); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(file, priv, f, false); +} + +static int vidioc_g_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct hantro_ctx *ctx = fh_to_ctx(priv); + + vpu_debug(4, "f->type = %d\n", f->type); + + *pix_mp = ctx->src_fmt; + + return 0; +} + +static int vidioc_g_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct hantro_ctx *ctx = fh_to_ctx(priv); + + vpu_debug(4, "f->type = %d\n", f->type); + + *pix_mp = ctx->dst_fmt; + + return 0; +} + +static int hantro_try_fmt(const struct hantro_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp, + enum v4l2_buf_type type) +{ + const struct hantro_fmt *fmt, *vpu_fmt; + bool capture = V4L2_TYPE_IS_CAPTURE(type); + bool coded; + + coded = capture == ctx->is_encoder; + + vpu_debug(4, "trying format %c%c%c%c\n", + (pix_mp->pixelformat & 0x7f), + (pix_mp->pixelformat >> 8) & 0x7f, + (pix_mp->pixelformat >> 16) & 0x7f, + (pix_mp->pixelformat >> 24) & 0x7f); + + fmt = hantro_find_format(ctx, pix_mp->pixelformat); + if (!fmt) { + fmt = hantro_get_default_fmt(ctx, coded); + pix_mp->pixelformat = fmt->fourcc; + } + + if (coded) { + pix_mp->num_planes = 1; + vpu_fmt = fmt; + } else if (ctx->is_encoder) { + vpu_fmt = ctx->vpu_dst_fmt; + } else { + vpu_fmt = fmt; + /* + * Width/height on the CAPTURE end of a decoder are ignored and + * replaced by the OUTPUT ones. + */ + pix_mp->width = ctx->src_fmt.width; + pix_mp->height = ctx->src_fmt.height; + } + + pix_mp->field = V4L2_FIELD_NONE; + + v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, + &vpu_fmt->frmsize); + + if (!coded) { + /* Fill remaining fields */ + v4l2_fill_pixfmt_mp(pix_mp, fmt->fourcc, pix_mp->width, + pix_mp->height); + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && + !hantro_needs_postproc(ctx, fmt)) + pix_mp->plane_fmt[0].sizeimage += + hantro_h264_mv_size(pix_mp->width, + pix_mp->height); + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME && + !hantro_needs_postproc(ctx, fmt)) + pix_mp->plane_fmt[0].sizeimage += + hantro_vp9_mv_size(pix_mp->width, + pix_mp->height); + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE && + !hantro_needs_postproc(ctx, fmt)) + pix_mp->plane_fmt[0].sizeimage += + hantro_hevc_mv_size(pix_mp->width, + pix_mp->height); + } else if (!pix_mp->plane_fmt[0].sizeimage) { + /* + * For coded formats the application can specify + * sizeimage. If the application passes a zero sizeimage, + * let's default to the maximum frame size. + */ + pix_mp->plane_fmt[0].sizeimage = fmt->header_size + + pix_mp->width * pix_mp->height * fmt->max_depth; + } + + return 0; +} + +static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); +} + +static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); +} + +static void +hantro_reset_fmt(struct v4l2_pix_format_mplane *fmt, + const struct hantro_fmt *vpu_fmt) +{ + memset(fmt, 0, sizeof(*fmt)); + + fmt->pixelformat = vpu_fmt->fourcc; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_JPEG; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static void +hantro_reset_encoded_fmt(struct hantro_ctx *ctx) +{ + const struct hantro_fmt *vpu_fmt; + struct v4l2_pix_format_mplane *fmt; + + vpu_fmt = hantro_get_default_fmt(ctx, true); + + if (ctx->is_encoder) { + ctx->vpu_dst_fmt = vpu_fmt; + fmt = &ctx->dst_fmt; + } else { + ctx->vpu_src_fmt = vpu_fmt; + fmt = &ctx->src_fmt; + } + + hantro_reset_fmt(fmt, vpu_fmt); + fmt->width = vpu_fmt->frmsize.min_width; + fmt->height = vpu_fmt->frmsize.min_height; + if (ctx->is_encoder) + hantro_set_fmt_cap(ctx, fmt); + else + hantro_set_fmt_out(ctx, fmt); +} + +static void +hantro_reset_raw_fmt(struct hantro_ctx *ctx) +{ + const struct hantro_fmt *raw_vpu_fmt; + struct v4l2_pix_format_mplane *raw_fmt, *encoded_fmt; + + raw_vpu_fmt = hantro_get_default_fmt(ctx, false); + + if (ctx->is_encoder) { + ctx->vpu_src_fmt = raw_vpu_fmt; + raw_fmt = &ctx->src_fmt; + encoded_fmt = &ctx->dst_fmt; + } else { + ctx->vpu_dst_fmt = raw_vpu_fmt; + raw_fmt = &ctx->dst_fmt; + encoded_fmt = &ctx->src_fmt; + } + + hantro_reset_fmt(raw_fmt, raw_vpu_fmt); + raw_fmt->width = encoded_fmt->width; + raw_fmt->height = encoded_fmt->height; + if (ctx->is_encoder) + hantro_set_fmt_out(ctx, raw_fmt); + else + hantro_set_fmt_cap(ctx, raw_fmt); +} + +void hantro_reset_fmts(struct hantro_ctx *ctx) +{ + hantro_reset_encoded_fmt(ctx); + hantro_reset_raw_fmt(ctx); +} + +static void +hantro_update_requires_request(struct hantro_ctx *ctx, u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_JPEG: + ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = false; + break; + case V4L2_PIX_FMT_MPEG2_SLICE: + case V4L2_PIX_FMT_VP8_FRAME: + case V4L2_PIX_FMT_H264_SLICE: + case V4L2_PIX_FMT_HEVC_SLICE: + case V4L2_PIX_FMT_VP9_FRAME: + ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true; + break; + default: + break; + } +} + +static void +hantro_update_requires_hold_capture_buf(struct hantro_ctx *ctx, u32 fourcc) +{ + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + switch (fourcc) { + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MPEG2_SLICE: + case V4L2_PIX_FMT_VP8_FRAME: + case V4L2_PIX_FMT_HEVC_SLICE: + case V4L2_PIX_FMT_VP9_FRAME: + vq->subsystem_flags &= ~(VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); + break; + case V4L2_PIX_FMT_H264_SLICE: + vq->subsystem_flags |= VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + break; + default: + break; + } +} + +static int hantro_set_fmt_out(struct hantro_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (ret) + return ret; + + if (!ctx->is_encoder) { + struct vb2_queue *peer_vq; + + /* + * In order to support dynamic resolution change, + * the decoder admits a resolution change, as long + * as the pixelformat remains. Can't be done if streaming. + */ + if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && + pix_mp->pixelformat != ctx->src_fmt.pixelformat)) + return -EBUSY; + /* + * Since format change on the OUTPUT queue will reset + * the CAPTURE queue, we can't allow doing so + * when the CAPTURE queue has buffers allocated. + */ + peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(peer_vq)) + return -EBUSY; + } else { + /* + * The encoder doesn't admit a format change if + * there are OUTPUT buffers allocated. + */ + if (vb2_is_busy(vq)) + return -EBUSY; + } + + ctx->vpu_src_fmt = hantro_find_format(ctx, pix_mp->pixelformat); + ctx->src_fmt = *pix_mp; + + /* + * Current raw format might have become invalid with newly + * selected codec, so reset it to default just to be safe and + * keep internal driver state sane. User is mandated to set + * the raw format again after we return, so we don't need + * anything smarter. + * Note that hantro_reset_raw_fmt() also propagates size + * changes to the raw format. + */ + if (!ctx->is_encoder) + hantro_reset_raw_fmt(ctx); + + /* Colorimetry information are always propagated. */ + ctx->dst_fmt.colorspace = pix_mp->colorspace; + ctx->dst_fmt.ycbcr_enc = pix_mp->ycbcr_enc; + ctx->dst_fmt.xfer_func = pix_mp->xfer_func; + ctx->dst_fmt.quantization = pix_mp->quantization; + + hantro_update_requires_request(ctx, pix_mp->pixelformat); + hantro_update_requires_hold_capture_buf(ctx, pix_mp->pixelformat); + + vpu_debug(0, "OUTPUT codec mode: %d\n", ctx->vpu_src_fmt->codec_mode); + vpu_debug(0, "fmt - w: %d, h: %d\n", + pix_mp->width, pix_mp->height); + return 0; +} + +static int hantro_set_fmt_cap(struct hantro_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + struct vb2_queue *vq; + int ret; + + /* Change not allowed if queue is busy. */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + if (ctx->is_encoder) { + struct vb2_queue *peer_vq; + + /* + * Since format change on the CAPTURE queue will reset + * the OUTPUT queue, we can't allow doing so + * when the OUTPUT queue has buffers allocated. + */ + peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_busy(peer_vq) && + (pix_mp->pixelformat != ctx->dst_fmt.pixelformat || + pix_mp->height != ctx->dst_fmt.height || + pix_mp->width != ctx->dst_fmt.width)) + return -EBUSY; + } + + ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (ret) + return ret; + + ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat); + ctx->dst_fmt = *pix_mp; + + /* + * Current raw format might have become invalid with newly + * selected codec, so reset it to default just to be safe and + * keep internal driver state sane. User is mandated to set + * the raw format again after we return, so we don't need + * anything smarter. + * Note that hantro_reset_raw_fmt() also propagates size + * changes to the raw format. + */ + if (ctx->is_encoder) + hantro_reset_raw_fmt(ctx); + + /* Colorimetry information are always propagated. */ + ctx->src_fmt.colorspace = pix_mp->colorspace; + ctx->src_fmt.ycbcr_enc = pix_mp->ycbcr_enc; + ctx->src_fmt.xfer_func = pix_mp->xfer_func; + ctx->src_fmt.quantization = pix_mp->quantization; + + vpu_debug(0, "CAPTURE codec mode: %d\n", ctx->vpu_dst_fmt->codec_mode); + vpu_debug(0, "fmt - w: %d, h: %d\n", + pix_mp->width, pix_mp->height); + + hantro_update_requires_request(ctx, pix_mp->pixelformat); + + return 0; +} + +static int +vidioc_s_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) +{ + return hantro_set_fmt_out(fh_to_ctx(priv), &f->fmt.pix_mp); +} + +static int +vidioc_s_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) +{ + return hantro_set_fmt_cap(fh_to_ctx(priv), &f->fmt.pix_mp); +} + +static int vidioc_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + + /* Crop only supported on source. */ + if (!ctx->is_encoder || + sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = ctx->src_fmt.width; + sel->r.height = ctx->src_fmt.height; + break; + case V4L2_SEL_TGT_CROP: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = ctx->dst_fmt.width; + sel->r.height = ctx->dst_fmt.height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + struct v4l2_rect *rect = &sel->r; + struct vb2_queue *vq; + + /* Crop only supported on source. */ + if (!ctx->is_encoder || + sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Change not allowed if the queue is streaming. */ + vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx); + if (vb2_is_streaming(vq)) + return -EBUSY; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * We do not support offsets, and we can crop only inside + * right-most or bottom-most macroblocks. + */ + if (rect->left != 0 || rect->top != 0 || + round_up(rect->width, MB_DIM) != ctx->src_fmt.width || + round_up(rect->height, MB_DIM) != ctx->src_fmt.height) { + /* Default to full frame for incorrect settings. */ + rect->left = 0; + rect->top = 0; + rect->width = ctx->src_fmt.width; + rect->height = ctx->src_fmt.height; + } else { + /* We support widths aligned to 4 pixels and arbitrary heights. */ + rect->width = round_up(rect->width, 4); + } + + ctx->dst_fmt.width = rect->width; + ctx->dst_fmt.height = rect->height; + + return 0; +} + +static const struct v4l2_event hantro_eos_event = { + .type = V4L2_EVENT_EOS +}; + +static int vidioc_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *ec) +{ + struct hantro_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, ec); + if (ret < 0) + return ret; + + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || + !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, priv, ec); + if (ret < 0) + return ret; + + if (ec->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); + + if (ec->cmd == V4L2_ENC_CMD_START) + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + + return 0; +} + +const struct v4l2_ioctl_ops hantro_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_out_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_cap_mplane, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_out_mplane, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_cap_mplane, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .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 = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = vidioc_encoder_cmd, +}; + +static int +hantro_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format_mplane *pixfmt; + int i; + + switch (vq->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + pixfmt = &ctx->dst_fmt; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pixfmt = &ctx->src_fmt; + break; + default: + vpu_err("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + if (*num_planes) { + if (*num_planes != pixfmt->num_planes) + return -EINVAL; + for (i = 0; i < pixfmt->num_planes; ++i) + if (sizes[i] < pixfmt->plane_fmt[i].sizeimage) + return -EINVAL; + return 0; + } + + *num_planes = pixfmt->num_planes; + for (i = 0; i < pixfmt->num_planes; ++i) + sizes[i] = pixfmt->plane_fmt[i].sizeimage; + return 0; +} + +static int +hantro_buf_plane_check(struct vb2_buffer *vb, + struct v4l2_pix_format_mplane *pixfmt) +{ + unsigned int sz; + int i; + + for (i = 0; i < pixfmt->num_planes; ++i) { + sz = pixfmt->plane_fmt[i].sizeimage; + vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", + i, vb2_plane_size(vb, i), sz); + if (vb2_plane_size(vb, i) < sz) { + vpu_err("plane %d is too small for output\n", i); + return -EINVAL; + } + } + return 0; +} + +static int hantro_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct hantro_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format_mplane *pix_fmt; + int ret; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + ret = hantro_buf_plane_check(vb, pix_fmt); + if (ret) + return ret; + /* + * Buffer's bytesused must be written by driver for CAPTURE buffers. + * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets + * it to buffer length). + */ + if (V4L2_TYPE_IS_CAPTURE(vq->type)) { + if (ctx->is_encoder) + vb2_set_plane_payload(vb, 0, 0); + else + vb2_set_plane_payload(vb, 0, pix_fmt->plane_fmt[0].sizeimage); + } + + return 0; +} + +static void hantro_buf_queue(struct vb2_buffer *vb) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb2_set_plane_payload(vb, i, 0); + + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = ctx->sequence_cap++; + + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); + return; + } + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static bool hantro_vq_is_coded(struct vb2_queue *q) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(q); + + return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); +} + +static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(q); + int ret = 0; + + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->sequence_out = 0; + else + ctx->sequence_cap = 0; + + if (hantro_vq_is_coded(q)) { + enum hantro_codec_mode codec_mode; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + codec_mode = ctx->vpu_src_fmt->codec_mode; + else + codec_mode = ctx->vpu_dst_fmt->codec_mode; + + vpu_debug(4, "Codec mode = %d\n", codec_mode); + ctx->codec_ops = &ctx->dev->variant->codec_ops[codec_mode]; + if (ctx->codec_ops->init) { + ret = ctx->codec_ops->init(ctx); + if (ret) + return ret; + } + + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) { + ret = hantro_postproc_alloc(ctx); + if (ret) + goto err_codec_exit; + } + } + return ret; + +err_codec_exit: + if (ctx->codec_ops->exit) + ctx->codec_ops->exit(ctx); + return ret; +} + +static void +hantro_return_bufs(struct vb2_queue *q, + struct vb2_v4l2_buffer *(*buf_remove)(struct v4l2_m2m_ctx *)) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(q); + + for (;;) { + struct vb2_v4l2_buffer *vbuf; + + vbuf = buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + break; + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } +} + +static void hantro_stop_streaming(struct vb2_queue *q) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(q); + + if (hantro_vq_is_coded(q)) { + hantro_postproc_free(ctx); + if (ctx->codec_ops && ctx->codec_ops->exit) + ctx->codec_ops->exit(ctx); + } + + /* + * The mem2mem framework calls v4l2_m2m_cancel_job before + * .stop_streaming, so there isn't any job running and + * it is safe to return all the buffers. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) + hantro_return_bufs(q, v4l2_m2m_src_buf_remove); + else + hantro_return_bufs(q, v4l2_m2m_dst_buf_remove); + + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); +} + +static void hantro_buf_request_complete(struct vb2_buffer *vb) +{ + struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler); +} + +static int hantro_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +const struct vb2_ops hantro_queue_ops = { + .queue_setup = hantro_queue_setup, + .buf_prepare = hantro_buf_prepare, + .buf_queue = hantro_buf_queue, + .buf_out_validate = hantro_buf_out_validate, + .buf_request_complete = hantro_buf_request_complete, + .start_streaming = hantro_start_streaming, + .stop_streaming = hantro_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.h b/drivers/media/platform/verisilicon/hantro_v4l2.h new file mode 100644 index 000000000000..64f6f57e9d7a --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_v4l2.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Alpha Lin + * Jeffy Chen + * + * Copyright 2018 Google LLC. + * Tomasz Figa + * + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#ifndef HANTRO_V4L2_H_ +#define HANTRO_V4L2_H_ + +#include "hantro.h" + +extern const struct v4l2_ioctl_ops hantro_ioctl_ops; +extern const struct vb2_ops hantro_queue_ops; + +void hantro_reset_fmts(struct hantro_ctx *ctx); +int hantro_get_format_depth(u32 fourcc); +const struct hantro_fmt * +hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream); + +#endif /* HANTRO_V4L2_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_vp8.c b/drivers/media/platform/verisilicon/hantro_vp8.c new file mode 100644 index 000000000000..381bc1d3bfda --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_vp8.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include "hantro.h" + +/* + * probs table with packed + */ +struct vp8_prob_tbl_packed { + u8 prob_mb_skip_false; + u8 prob_intra; + u8 prob_ref_last; + u8 prob_ref_golden; + u8 prob_segment[3]; + u8 padding0; + + u8 prob_luma_16x16_pred_mode[4]; + u8 prob_chroma_pred_mode[3]; + u8 padding1; + + /* mv prob */ + u8 prob_mv_context[2][V4L2_VP8_MV_PROB_CNT]; + u8 padding2[2]; + + /* coeff probs */ + u8 prob_coeffs[4][8][3][V4L2_VP8_COEFF_PROB_CNT]; + u8 padding3[96]; +}; + +/* + * filter taps taken to 7-bit precision, + * reference RFC6386#Page-16, filters[8][6] + */ +const u32 hantro_vp8_dec_mc_filter[8][6] = { + { 0, 0, 128, 0, 0, 0 }, + { 0, -6, 123, 12, -1, 0 }, + { 2, -11, 108, 36, -8, 1 }, + { 0, -9, 93, 50, -6, 0 }, + { 3, -16, 77, 77, -16, 3 }, + { 0, -6, 50, 93, -9, 0 }, + { 1, -8, 36, 108, -11, 2 }, + { 0, -1, 12, 123, -6, 0 } +}; + +void hantro_vp8_prob_update(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + const struct v4l2_vp8_entropy *entropy = &hdr->entropy; + u32 i, j, k; + u8 *dst; + + /* first probs */ + dst = ctx->vp8_dec.prob_tbl.cpu; + + dst[0] = hdr->prob_skip_false; + dst[1] = hdr->prob_intra; + dst[2] = hdr->prob_last; + dst[3] = hdr->prob_gf; + dst[4] = hdr->segment.segment_probs[0]; + dst[5] = hdr->segment.segment_probs[1]; + dst[6] = hdr->segment.segment_probs[2]; + dst[7] = 0; + + dst += 8; + dst[0] = entropy->y_mode_probs[0]; + dst[1] = entropy->y_mode_probs[1]; + dst[2] = entropy->y_mode_probs[2]; + dst[3] = entropy->y_mode_probs[3]; + dst[4] = entropy->uv_mode_probs[0]; + dst[5] = entropy->uv_mode_probs[1]; + dst[6] = entropy->uv_mode_probs[2]; + dst[7] = 0; /*unused */ + + /* mv probs */ + dst += 8; + dst[0] = entropy->mv_probs[0][0]; /* is short */ + dst[1] = entropy->mv_probs[1][0]; + dst[2] = entropy->mv_probs[0][1]; /* sign */ + dst[3] = entropy->mv_probs[1][1]; + dst[4] = entropy->mv_probs[0][8 + 9]; + dst[5] = entropy->mv_probs[0][9 + 9]; + dst[6] = entropy->mv_probs[1][8 + 9]; + dst[7] = entropy->mv_probs[1][9 + 9]; + dst += 8; + for (i = 0; i < 2; ++i) { + for (j = 0; j < 8; j += 4) { + dst[0] = entropy->mv_probs[i][j + 9 + 0]; + dst[1] = entropy->mv_probs[i][j + 9 + 1]; + dst[2] = entropy->mv_probs[i][j + 9 + 2]; + dst[3] = entropy->mv_probs[i][j + 9 + 3]; + dst += 4; + } + } + for (i = 0; i < 2; ++i) { + dst[0] = entropy->mv_probs[i][0 + 2]; + dst[1] = entropy->mv_probs[i][1 + 2]; + dst[2] = entropy->mv_probs[i][2 + 2]; + dst[3] = entropy->mv_probs[i][3 + 2]; + dst[4] = entropy->mv_probs[i][4 + 2]; + dst[5] = entropy->mv_probs[i][5 + 2]; + dst[6] = entropy->mv_probs[i][6 + 2]; + dst[7] = 0; /*unused */ + dst += 8; + } + + /* coeff probs (header part) */ + dst = ctx->vp8_dec.prob_tbl.cpu; + dst += (8 * 7); + for (i = 0; i < 4; ++i) { + for (j = 0; j < 8; ++j) { + for (k = 0; k < 3; ++k) { + dst[0] = entropy->coeff_probs[i][j][k][0]; + dst[1] = entropy->coeff_probs[i][j][k][1]; + dst[2] = entropy->coeff_probs[i][j][k][2]; + dst[3] = entropy->coeff_probs[i][j][k][3]; + dst += 4; + } + } + } + + /* coeff probs (footer part) */ + dst = ctx->vp8_dec.prob_tbl.cpu; + dst += (8 * 55); + for (i = 0; i < 4; ++i) { + for (j = 0; j < 8; ++j) { + for (k = 0; k < 3; ++k) { + dst[0] = entropy->coeff_probs[i][j][k][4]; + dst[1] = entropy->coeff_probs[i][j][k][5]; + dst[2] = entropy->coeff_probs[i][j][k][6]; + dst[3] = entropy->coeff_probs[i][j][k][7]; + dst[4] = entropy->coeff_probs[i][j][k][8]; + dst[5] = entropy->coeff_probs[i][j][k][9]; + dst[6] = entropy->coeff_probs[i][j][k][10]; + dst[7] = 0; /*unused */ + dst += 8; + } + } + } +} + +int hantro_vp8_dec_init(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_aux_buf *aux_buf; + unsigned int mb_width, mb_height; + size_t segment_map_size; + int ret; + + /* segment map table size calculation */ + mb_width = DIV_ROUND_UP(ctx->dst_fmt.width, 16); + mb_height = DIV_ROUND_UP(ctx->dst_fmt.height, 16); + segment_map_size = round_up(DIV_ROUND_UP(mb_width * mb_height, 4), 64); + + /* + * In context init the dma buffer for segment map must be allocated. + * And the data in segment map buffer must be set to all zero. + */ + aux_buf = &ctx->vp8_dec.segment_map; + aux_buf->size = segment_map_size; + aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, + &aux_buf->dma, GFP_KERNEL); + if (!aux_buf->cpu) + return -ENOMEM; + + /* + * Allocate probability table buffer, + * total 1208 bytes, 4K page is far enough. + */ + aux_buf = &ctx->vp8_dec.prob_tbl; + aux_buf->size = sizeof(struct vp8_prob_tbl_packed); + aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, + &aux_buf->dma, GFP_KERNEL); + if (!aux_buf->cpu) { + ret = -ENOMEM; + goto err_free_seg_map; + } + + return 0; + +err_free_seg_map: + dma_free_coherent(vpu->dev, ctx->vp8_dec.segment_map.size, + ctx->vp8_dec.segment_map.cpu, + ctx->vp8_dec.segment_map.dma); + + return ret; +} + +void hantro_vp8_dec_exit(struct hantro_ctx *ctx) +{ + struct hantro_vp8_dec_hw_ctx *vp8_dec = &ctx->vp8_dec; + struct hantro_dev *vpu = ctx->dev; + + dma_free_coherent(vpu->dev, vp8_dec->segment_map.size, + vp8_dec->segment_map.cpu, vp8_dec->segment_map.dma); + dma_free_coherent(vpu->dev, vp8_dec->prob_tbl.size, + vp8_dec->prob_tbl.cpu, vp8_dec->prob_tbl.dma); +} diff --git a/drivers/media/platform/verisilicon/hantro_vp9.c b/drivers/media/platform/verisilicon/hantro_vp9.c new file mode 100644 index 000000000000..566cd376c097 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_vp9.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VP9 codec driver + * + * Copyright (C) 2021 Collabora Ltd. + */ + +#include +#include + +#include "hantro.h" +#include "hantro_hw.h" +#include "hantro_vp9.h" + +#define POW2(x) (1 << (x)) + +#define MAX_LOG2_TILE_COLUMNS 6 +#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS) +#define MAX_TILE_COLS 20 +#define MAX_TILE_ROWS 22 + +static size_t hantro_vp9_tile_filter_size(unsigned int height) +{ + u32 h, height32, size; + + h = roundup(height, 8); + + height32 = roundup(h, 64); + size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 8 + 8 */ + + return size; +} + +static size_t hantro_vp9_bsd_control_size(unsigned int height) +{ + u32 h, height32; + + h = roundup(height, 8); + height32 = roundup(h, 64); + + return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1); +} + +static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int height) +{ + u32 w, h; + int num_ctbs; + + w = roundup(width, 8); + h = roundup(height, 8); + num_ctbs = ((w + 63) / 64) * ((h + 63) / 64); + + return num_ctbs * 32; +} + +static inline size_t hantro_vp9_prob_tab_size(void) +{ + return roundup(sizeof(struct hantro_g2_all_probs), 16); +} + +static inline size_t hantro_vp9_count_tab_size(void) +{ + return roundup(sizeof(struct symbol_counts), 16); +} + +static inline size_t hantro_vp9_tile_info_size(void) +{ + return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 15 + 16) & ~0xf, 16); +} + +static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, int l, int m) +{ + if (i == 0) + return &cnts->count_coeffs[j][k][l][m]; + + if (i == 1) + return &cnts->count_coeffs8x8[j][k][l][m]; + + if (i == 2) + return &cnts->count_coeffs16x16[j][k][l][m]; + + if (i == 3) + return &cnts->count_coeffs32x32[j][k][l][m]; + + return NULL; +} + +static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int l, int m) +{ + if (i == 0) + return &cnts->count_coeffs[j][k][l][m][3]; + + if (i == 1) + return &cnts->count_coeffs8x8[j][k][l][m][3]; + + if (i == 2) + return &cnts->count_coeffs16x16[j][k][l][m][3]; + + if (i == 3) + return &cnts->count_coeffs32x32[j][k][l][m][3]; + + return NULL; +} + +#define INNER_LOOP \ + do { \ + for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0][0]); ++m) { \ + vp9_ctx->cnts.coeff[i][j][k][l][m] = \ + get_coeffs_arr(cnts, i, j, k, l, m); \ + vp9_ctx->cnts.eob[i][j][k][l][m][0] = \ + &cnts->count_eobs[i][j][k][l][m]; \ + vp9_ctx->cnts.eob[i][j][k][l][m][1] = \ + get_eobs1(cnts, i, j, k, l, m); \ + } \ + } while (0) + +static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx) +{ + struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; + struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; + int i, j, k, l, m; + + vp9_ctx->cnts.partition = &cnts->partition_counts; + vp9_ctx->cnts.skip = &cnts->mbskip_count; + vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count; + vp9_ctx->cnts.tx32p = &cnts->tx32x32_count; + /* + * g2 hardware uses tx16x16_count[2][3], while the api + * expects tx16p[2][4], so this must be explicitly copied + * into vp9_ctx->cnts.tx16p when passing the data to the + * vp9 library function + */ + vp9_ctx->cnts.tx8p = &cnts->tx8x8_count; + + vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts; + vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts; + vp9_ctx->cnts.comp = &cnts->comp_inter_count; + vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count; + vp9_ctx->cnts.single_ref = &cnts->single_ref_count; + vp9_ctx->cnts.filter = &cnts->switchable_interp_counts; + vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints; + vp9_ctx->cnts.sign = &cnts->mv_counts.sign; + vp9_ctx->cnts.classes = &cnts->mv_counts.classes; + vp9_ctx->cnts.class0 = &cnts->mv_counts.class0; + vp9_ctx->cnts.bits = &cnts->mv_counts.bits; + vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp; + vp9_ctx->cnts.fp = &cnts->mv_counts.fp; + vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp; + vp9_ctx->cnts.hp = &cnts->mv_counts.hp; + + for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i) + for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j) + for (k = 0; k < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0]); ++k) + for (l = 0; l < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l) + INNER_LOOP; +} + +int hantro_vp9_dec_init(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + const struct hantro_variant *variant = vpu->variant; + struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; + struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; + struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; + struct hantro_aux_buf *misc = &vp9_dec->misc; + u32 i, max_width, max_height, size; + + if (variant->num_dec_fmts < 1) + return -EINVAL; + + for (i = 0; i < variant->num_dec_fmts; ++i) + if (variant->dec_fmts[i].fourcc == V4L2_PIX_FMT_VP9_FRAME) + break; + + if (i == variant->num_dec_fmts) + return -EINVAL; + + max_width = vpu->variant->dec_fmts[i].frmsize.max_width; + max_height = vpu->variant->dec_fmts[i].frmsize.max_height; + + size = hantro_vp9_tile_filter_size(max_height); + vp9_dec->bsd_ctrl_offset = size; + size += hantro_vp9_bsd_control_size(max_height); + + tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge->dma, GFP_KERNEL); + if (!tile_edge->cpu) + return -ENOMEM; + + tile_edge->size = size; + memset(tile_edge->cpu, 0, size); + + size = hantro_vp9_segment_map_size(max_width, max_height); + vp9_dec->segment_map_size = size; + size *= 2; /* we need two areas of this size, used alternately */ + + segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map->dma, GFP_KERNEL); + if (!segment_map->cpu) + goto err_segment_map; + + segment_map->size = size; + memset(segment_map->cpu, 0, size); + + size = hantro_vp9_prob_tab_size(); + vp9_dec->ctx_counters_offset = size; + size += hantro_vp9_count_tab_size(); + vp9_dec->tile_info_offset = size; + size += hantro_vp9_tile_info_size(); + + misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, GFP_KERNEL); + if (!misc->cpu) + goto err_misc; + + misc->size = size; + memset(misc->cpu, 0, size); + + init_v4l2_vp9_count_tbl(ctx); + + return 0; + +err_misc: + dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); + +err_segment_map: + dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); + + return -ENOMEM; +} + +void hantro_vp9_dec_exit(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; + struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; + struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; + struct hantro_aux_buf *misc = &vp9_dec->misc; + + dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma); + dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); + dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); +} diff --git a/drivers/media/platform/verisilicon/hantro_vp9.h b/drivers/media/platform/verisilicon/hantro_vp9.h new file mode 100644 index 000000000000..26b69275f098 --- /dev/null +++ b/drivers/media/platform/verisilicon/hantro_vp9.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VP9 codec driver + * + * Copyright (C) 2021 Collabora Ltd. + */ + +struct hantro_g2_mv_probs { + u8 joint[3]; + u8 sign[2]; + u8 class0_bit[2][1]; + u8 fr[2][3]; + u8 class0_hp[2]; + u8 hp[2]; + u8 classes[2][10]; + u8 class0_fr[2][2][3]; + u8 bits[2][10]; +}; + +struct hantro_g2_probs { + u8 inter_mode[7][4]; + u8 is_inter[4]; + u8 uv_mode[10][8]; + u8 tx8[2][1]; + u8 tx16[2][2]; + u8 tx32[2][3]; + u8 y_mode_tail[4][1]; + u8 y_mode[4][8]; + u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */ + u8 uv_mode_tail[10][1]; + u8 interp_filter[4][2]; + u8 comp_mode[5]; + u8 skip[3]; + + u8 pad1[1]; + + struct hantro_g2_mv_probs mv; + + u8 single_ref[5][2]; + u8 comp_ref[5]; + + u8 pad2[17]; + + u8 coef[4][2][2][6][6][4]; +}; + +struct hantro_g2_all_probs { + u8 kf_y_mode_prob[10][10][8]; + + u8 kf_y_mode_prob_tail[10][10][1]; + u8 ref_pred_probs[3]; + u8 mb_segment_tree_probs[7]; + u8 segment_pred_probs[3]; + u8 ref_scores[4]; + u8 prob_comppred[2]; + + u8 pad1[9]; + + u8 kf_uv_mode_prob[10][8]; + u8 kf_uv_mode_prob_tail[10][1]; + + u8 pad2[6]; + + struct hantro_g2_probs probs; +}; + +struct mv_counts { + u32 joints[4]; + u32 sign[2][2]; + u32 classes[2][11]; + u32 class0[2][2]; + u32 bits[2][10][2]; + u32 class0_fp[2][2][4]; + u32 fp[2][4]; + u32 class0_hp[2][2]; + u32 hp[2][2]; +}; + +struct symbol_counts { + u32 inter_mode_counts[7][3][2]; + u32 sb_ymode_counts[4][10]; + u32 uv_mode_counts[10][10]; + u32 partition_counts[16][4]; + u32 switchable_interp_counts[4][3]; + u32 intra_inter_count[4][2]; + u32 comp_inter_count[5][2]; + u32 single_ref_count[5][2][2]; + u32 comp_ref_count[5][2]; + u32 tx32x32_count[2][4]; + u32 tx16x16_count[2][3]; + u32 tx8x8_count[2][2]; + u32 mbskip_count[3][2]; + + struct mv_counts mv_counts; + + u32 count_coeffs[2][2][6][6][4]; + u32 count_coeffs8x8[2][2][6][6][4]; + u32 count_coeffs16x16[2][2][6][6][4]; + u32 count_coeffs32x32[2][2][6][6][4]; + + u32 count_eobs[4][2][2][6][6]; +}; diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c new file mode 100644 index 000000000000..77f574fdfa77 --- /dev/null +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include +#include + +#include "hantro.h" +#include "hantro_jpeg.h" +#include "hantro_g1_regs.h" +#include "hantro_g2_regs.h" + +#define CTRL_SOFT_RESET 0x00 +#define RESET_G1 BIT(1) +#define RESET_G2 BIT(0) + +#define CTRL_CLOCK_ENABLE 0x04 +#define CLOCK_G1 BIT(1) +#define CLOCK_G2 BIT(0) + +#define CTRL_G1_DEC_FUSE 0x08 +#define CTRL_G1_PP_FUSE 0x0c +#define CTRL_G2_DEC_FUSE 0x10 + +static void imx8m_soft_reset(struct hantro_dev *vpu, u32 reset_bits) +{ + u32 val; + + /* Assert */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val &= ~reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); + + udelay(2); + + /* Release */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val |= reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); +} + +static void imx8m_clk_enable(struct hantro_dev *vpu, u32 clock_bits) +{ + u32 val; + + val = readl(vpu->ctrl_base + CTRL_CLOCK_ENABLE); + val |= clock_bits; + writel(val, vpu->ctrl_base + CTRL_CLOCK_ENABLE); +} + +static int imx8mq_runtime_resume(struct hantro_dev *vpu) +{ + int ret; + + ret = clk_bulk_prepare_enable(vpu->variant->num_clocks, vpu->clocks); + if (ret) { + dev_err(vpu->dev, "Failed to enable clocks\n"); + return ret; + } + + imx8m_soft_reset(vpu, RESET_G1 | RESET_G2); + imx8m_clk_enable(vpu, CLOCK_G1 | CLOCK_G2); + + /* Set values of the fuse registers */ + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_DEC_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_PP_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G2_DEC_FUSE); + + clk_bulk_disable_unprepare(vpu->variant->num_clocks, vpu->clocks); + + return 0; +} + +/* + * Supported formats. + */ + +static const struct hantro_fmt imx8m_vpu_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt imx8m_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12_4L4, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = TILE_MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = TILE_MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .codec_mode = HANTRO_MODE_HEVC_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = TILE_MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = TILE_MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP9_FRAME, + .codec_mode = HANTRO_MODE_VP9_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = TILE_MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = TILE_MB_DIM, + }, + }, +}; + +static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, G1_REG_INTERRUPT); + state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} + +static int imx8mq_vpu_hw_init(struct hantro_dev *vpu) +{ + vpu->ctrl_base = vpu->reg_bases[vpu->variant->num_regs - 1]; + + return 0; +} + +static void imx8m_vpu_g1_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + imx8m_soft_reset(vpu, RESET_G1); +} + +/* + * Supported codec ops. + */ + +static const struct hantro_codec_ops imx8mq_vpu_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + +static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + +static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = { + [HANTRO_MODE_HEVC_DEC] = { + .run = hantro_g2_hevc_dec_run, + .init = hantro_hevc_dec_init, + .exit = hantro_hevc_dec_exit, + }, + [HANTRO_MODE_VP9_DEC] = { + .run = hantro_g2_vp9_dec_run, + .done = hantro_g2_vp9_dec_done, + .init = hantro_vp9_dec_init, + .exit = hantro_vp9_dec_exit, + }, +}; + +/* + * VPU variants. + */ + +static const struct hantro_irq imx8mq_irqs[] = { + { "g1", imx8m_vpu_g1_irq }, +}; + +static const struct hantro_irq imx8mq_g2_irqs[] = { + { "g2", hantro_g2_irq }, +}; + +static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; +static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; +static const char * const imx8mq_g1_clk_names[] = { "g1" }; +static const char * const imx8mq_g2_clk_names[] = { "g2" }; + +const struct hantro_variant imx8mq_vpu_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .postproc_fmts = imx8m_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_codec_ops, + .init = imx8mq_vpu_hw_init, + .runtime_resume = imx8mq_runtime_resume, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_clk_names), + .reg_names = imx8mq_reg_names, + .num_regs = ARRAY_SIZE(imx8mq_reg_names) +}; + +const struct hantro_variant imx8mq_vpu_g1_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .postproc_fmts = imx8m_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_g1_codec_ops, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_g1_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), +}; + +const struct hantro_variant imx8mq_vpu_g2_variant = { + .dec_offset = 0x0, + .dec_fmts = imx8m_vpu_g2_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts), + .postproc_fmts = imx8m_vpu_g2_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_g2_postproc_fmts), + .postproc_ops = &hantro_g2_postproc_ops, + .codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER, + .codec_ops = imx8mq_vpu_g2_codec_ops, + .irqs = imx8mq_g2_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_g2_irqs), + .clk_names = imx8mq_g2_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names), +}; + +const struct hantro_variant imx8mm_vpu_g1_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_g1_codec_ops, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_g1_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), +}; diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c new file mode 100644 index 000000000000..46c1a83bcc4e --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * Herman Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + */ + +#include +#include + +#include + +#include "hantro_hw.h" +#include "hantro_v4l2.h" + +#define VDPU_SWREG(nr) ((nr) * 4) + +#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) +#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) +#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) +#define VDPU_REG_DIR_MV_BASE VDPU_SWREG(62) +#define VDPU_REG_REFER_BASE(i) (VDPU_SWREG(84 + (i))) +#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) +#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) +#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) +#define VDPU_REG_PIC_FIXED_QUANT(v) ((v) ? BIT(7) : 0) +#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) + +#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) +#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) + +#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) +#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) +#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) + +#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) + +#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) +#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) +#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) +#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) +#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) +#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) +#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) +#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) + +#define VDPU_REG_START_CODE_E(v) ((v) ? BIT(22) : 0) +#define VDPU_REG_CH_8PIX_ILEAV_E(v) ((v) ? BIT(21) : 0) +#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) +#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) +#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) +#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) +#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) +#define VDPU_REG_SEQ_MBAFF_E(v) ((v) ? BIT(7) : 0) +#define VDPU_REG_PICORD_COUNT_E(v) ((v) ? BIT(6) : 0) +#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) + +#define VDPU_REG_PRED_BC_TAP_0_0(v) (((v) << 22) & GENMASK(31, 22)) +#define VDPU_REG_PRED_BC_TAP_0_1(v) (((v) << 12) & GENMASK(21, 12)) +#define VDPU_REG_PRED_BC_TAP_0_2(v) (((v) << 2) & GENMASK(11, 2)) + +#define VDPU_REG_REFBU_E(v) ((v) ? BIT(31) : 0) + +#define VDPU_REG_PINIT_RLIST_F9(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_PINIT_RLIST_F8(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_PINIT_RLIST_F7(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_PINIT_RLIST_F6(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_PINIT_RLIST_F5(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_PINIT_RLIST_F4(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_PINIT_RLIST_F15(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_PINIT_RLIST_F14(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_PINIT_RLIST_F13(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_PINIT_RLIST_F12(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_PINIT_RLIST_F11(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_PINIT_RLIST_F10(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_REFER1_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER0_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER3_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER2_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER5_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER4_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER7_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER6_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER9_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER8_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER11_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER10_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER13_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER12_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFER15_NBR(v) (((v) << 16) & GENMASK(31, 16)) +#define VDPU_REG_REFER14_NBR(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_BINIT_RLIST_F5(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_BINIT_RLIST_F4(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_BINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_BINIT_RLIST_F11(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_BINIT_RLIST_F10(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_BINIT_RLIST_F9(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_F8(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_F7(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_F6(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_BINIT_RLIST_F15(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_F14(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_F13(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_F12(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_BINIT_RLIST_B5(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_BINIT_RLIST_B4(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_BINIT_RLIST_B3(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_B2(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_B1(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_B0(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_BINIT_RLIST_B11(v) (((v) << 25) & GENMASK(29, 25)) +#define VDPU_REG_BINIT_RLIST_B10(v) (((v) << 20) & GENMASK(24, 20)) +#define VDPU_REG_BINIT_RLIST_B9(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_B8(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_B7(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_B6(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_BINIT_RLIST_B15(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_BINIT_RLIST_B14(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_BINIT_RLIST_B13(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_BINIT_RLIST_B12(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_PINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) +#define VDPU_REG_PINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) +#define VDPU_REG_PINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) +#define VDPU_REG_PINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_REFER_LTERM_E(v) (((v) << 0) & GENMASK(31, 0)) + +#define VDPU_REG_REFER_VALID_E(v) (((v) << 0) & GENMASK(31, 0)) + +#define VDPU_REG_STRM_START_BIT(v) (((v) << 0) & GENMASK(5, 0)) + +#define VDPU_REG_CH_QP_OFFSET2(v) (((v) << 22) & GENMASK(26, 22)) +#define VDPU_REG_CH_QP_OFFSET(v) (((v) << 17) & GENMASK(21, 17)) +#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 9) & GENMASK(16, 9)) +#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) + +#define VDPU_REG_WEIGHT_BIPR_IDC(v) (((v) << 16) & GENMASK(17, 16)) +#define VDPU_REG_REF_FRAMES(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_FILT_CTRL_PRES(v) ((v) ? BIT(31) : 0) +#define VDPU_REG_RDPIC_CNT_PRES(v) ((v) ? BIT(30) : 0) +#define VDPU_REG_FRAMENUM_LEN(v) (((v) << 16) & GENMASK(20, 16)) +#define VDPU_REG_FRAMENUM(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_REFPIC_MK_LEN(v) (((v) << 16) & GENMASK(26, 16)) +#define VDPU_REG_IDR_PIC_ID(v) (((v) << 0) & GENMASK(15, 0)) + +#define VDPU_REG_PPS_ID(v) (((v) << 24) & GENMASK(31, 24)) +#define VDPU_REG_REFIDX1_ACTIVE(v) (((v) << 19) & GENMASK(23, 19)) +#define VDPU_REG_REFIDX0_ACTIVE(v) (((v) << 14) & GENMASK(18, 14)) +#define VDPU_REG_POC_LENGTH(v) (((v) << 0) & GENMASK(7, 0)) + +#define VDPU_REG_IDR_PIC_E(v) ((v) ? BIT(8) : 0) +#define VDPU_REG_DIR_8X8_INFER_E(v) ((v) ? BIT(7) : 0) +#define VDPU_REG_BLACKWHITE_E(v) ((v) ? BIT(6) : 0) +#define VDPU_REG_CABAC_E(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_WEIGHT_PRED_E(v) ((v) ? BIT(4) : 0) +#define VDPU_REG_CONST_INTRA_E(v) ((v) ? BIT(3) : 0) +#define VDPU_REG_8X8TRANS_FLAG_E(v) ((v) ? BIT(2) : 0) +#define VDPU_REG_TYPE1_QUANT_E(v) ((v) ? BIT(1) : 0) +#define VDPU_REG_FIELDPIC_FLAG_E(v) ((v) ? BIT(0) : 0) + +static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; + const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; + const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; + struct hantro_dev *vpu = ctx->dev; + u32 reg; + + reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | + VDPU_REG_DEC_SCMD_DIS(0) | + VDPU_REG_FILTERING_DIS(0) | + VDPU_REG_PIC_FIXED_QUANT(0) | + VDPU_REG_DEC_LATENCY(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); + + reg = VDPU_REG_INIT_QP(pps->pic_init_qp_minus26 + 26) | + VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); + + reg = VDPU_REG_APF_THRESHOLD(8) | + VDPU_REG_STARTMB_X(0) | + VDPU_REG_STARTMB_Y(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); + + reg = VDPU_REG_DEC_MODE(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); + + reg = VDPU_REG_DEC_STRENDIAN_E(1) | + VDPU_REG_DEC_STRSWAP32_E(1) | + VDPU_REG_DEC_OUTSWAP32_E(1) | + VDPU_REG_DEC_INSWAP32_E(1) | + VDPU_REG_DEC_OUT_ENDIAN(1) | + VDPU_REG_DEC_IN_ENDIAN(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); + + reg = VDPU_REG_DEC_DATA_DISC_E(0) | + VDPU_REG_DEC_MAX_BURST(16) | + VDPU_REG_DEC_AXI_WR_ID(0) | + VDPU_REG_DEC_AXI_RD_ID(0xff); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); + + reg = VDPU_REG_START_CODE_E(1) | + VDPU_REG_CH_8PIX_ILEAV_E(0) | + VDPU_REG_RLC_MODE_E(0) | + VDPU_REG_PIC_INTERLACE_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && + (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || + dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) | + VDPU_REG_PIC_FIELDMODE_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) | + VDPU_REG_PIC_TOPFIELD_E(!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) | + VDPU_REG_WRITE_MVS_E((sps->profile_idc > 66) && dec_param->nal_ref_idc) | + VDPU_REG_SEQ_MBAFF_E(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) | + VDPU_REG_PICORD_COUNT_E(sps->profile_idc > 66) | + VDPU_REG_DEC_TIMEOUT_E(1) | + VDPU_REG_DEC_CLK_GATE_E(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); + + reg = VDPU_REG_PRED_BC_TAP_0_0(1) | + VDPU_REG_PRED_BC_TAP_0_1((u32)-5) | + VDPU_REG_PRED_BC_TAP_0_2(20); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(59)); + + reg = VDPU_REG_REFBU_E(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(65)); + + reg = VDPU_REG_STRM_START_BIT(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(109)); + + reg = VDPU_REG_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset) | + VDPU_REG_CH_QP_OFFSET(pps->chroma_qp_index_offset) | + VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | + VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(110)); + + reg = VDPU_REG_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc) | + VDPU_REG_REF_FRAMES(sps->max_num_ref_frames); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(111)); + + reg = VDPU_REG_FILT_CTRL_PRES(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) | + VDPU_REG_RDPIC_CNT_PRES(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) | + VDPU_REG_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | + VDPU_REG_FRAMENUM(dec_param->frame_num); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(112)); + + reg = VDPU_REG_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | + VDPU_REG_IDR_PIC_ID(dec_param->idr_pic_id); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(113)); + + reg = VDPU_REG_PPS_ID(pps->pic_parameter_set_id) | + VDPU_REG_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | + VDPU_REG_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | + VDPU_REG_POC_LENGTH(dec_param->pic_order_cnt_bit_size); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(114)); + + reg = VDPU_REG_IDR_PIC_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) | + VDPU_REG_DIR_8X8_INFER_E(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) | + VDPU_REG_BLACKWHITE_E(sps->profile_idc >= 100 && sps->chroma_format_idc == 0) | + VDPU_REG_CABAC_E(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) | + VDPU_REG_WEIGHT_PRED_E(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) | + VDPU_REG_CONST_INTRA_E(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) | + VDPU_REG_8X8TRANS_FLAG_E(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) | + VDPU_REG_TYPE1_QUANT_E(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) | + VDPU_REG_FIELDPIC_FLAG_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(115)); +} + +static void set_ref(struct hantro_ctx *ctx) +{ + const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; + struct hantro_dev *vpu = ctx->dev; + u32 reg; + int i; + + b0_reflist = ctx->h264_dec.reflists.b0; + b1_reflist = ctx->h264_dec.reflists.b1; + p_reflist = ctx->h264_dec.reflists.p; + + reg = VDPU_REG_PINIT_RLIST_F9(p_reflist[9].index) | + VDPU_REG_PINIT_RLIST_F8(p_reflist[8].index) | + VDPU_REG_PINIT_RLIST_F7(p_reflist[7].index) | + VDPU_REG_PINIT_RLIST_F6(p_reflist[6].index) | + VDPU_REG_PINIT_RLIST_F5(p_reflist[5].index) | + VDPU_REG_PINIT_RLIST_F4(p_reflist[4].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(74)); + + reg = VDPU_REG_PINIT_RLIST_F15(p_reflist[15].index) | + VDPU_REG_PINIT_RLIST_F14(p_reflist[14].index) | + VDPU_REG_PINIT_RLIST_F13(p_reflist[13].index) | + VDPU_REG_PINIT_RLIST_F12(p_reflist[12].index) | + VDPU_REG_PINIT_RLIST_F11(p_reflist[11].index) | + VDPU_REG_PINIT_RLIST_F10(p_reflist[10].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(75)); + + reg = VDPU_REG_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, 1)) | + VDPU_REG_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, 0)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(76)); + + reg = VDPU_REG_REFER3_NBR(hantro_h264_get_ref_nbr(ctx, 3)) | + VDPU_REG_REFER2_NBR(hantro_h264_get_ref_nbr(ctx, 2)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(77)); + + reg = VDPU_REG_REFER5_NBR(hantro_h264_get_ref_nbr(ctx, 5)) | + VDPU_REG_REFER4_NBR(hantro_h264_get_ref_nbr(ctx, 4)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(78)); + + reg = VDPU_REG_REFER7_NBR(hantro_h264_get_ref_nbr(ctx, 7)) | + VDPU_REG_REFER6_NBR(hantro_h264_get_ref_nbr(ctx, 6)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(79)); + + reg = VDPU_REG_REFER9_NBR(hantro_h264_get_ref_nbr(ctx, 9)) | + VDPU_REG_REFER8_NBR(hantro_h264_get_ref_nbr(ctx, 8)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(80)); + + reg = VDPU_REG_REFER11_NBR(hantro_h264_get_ref_nbr(ctx, 11)) | + VDPU_REG_REFER10_NBR(hantro_h264_get_ref_nbr(ctx, 10)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(81)); + + reg = VDPU_REG_REFER13_NBR(hantro_h264_get_ref_nbr(ctx, 13)) | + VDPU_REG_REFER12_NBR(hantro_h264_get_ref_nbr(ctx, 12)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(82)); + + reg = VDPU_REG_REFER15_NBR(hantro_h264_get_ref_nbr(ctx, 15)) | + VDPU_REG_REFER14_NBR(hantro_h264_get_ref_nbr(ctx, 14)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(83)); + + reg = VDPU_REG_BINIT_RLIST_F5(b0_reflist[5].index) | + VDPU_REG_BINIT_RLIST_F4(b0_reflist[4].index) | + VDPU_REG_BINIT_RLIST_F3(b0_reflist[3].index) | + VDPU_REG_BINIT_RLIST_F2(b0_reflist[2].index) | + VDPU_REG_BINIT_RLIST_F1(b0_reflist[1].index) | + VDPU_REG_BINIT_RLIST_F0(b0_reflist[0].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(100)); + + reg = VDPU_REG_BINIT_RLIST_F11(b0_reflist[11].index) | + VDPU_REG_BINIT_RLIST_F10(b0_reflist[10].index) | + VDPU_REG_BINIT_RLIST_F9(b0_reflist[9].index) | + VDPU_REG_BINIT_RLIST_F8(b0_reflist[8].index) | + VDPU_REG_BINIT_RLIST_F7(b0_reflist[7].index) | + VDPU_REG_BINIT_RLIST_F6(b0_reflist[6].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(101)); + + reg = VDPU_REG_BINIT_RLIST_F15(b0_reflist[15].index) | + VDPU_REG_BINIT_RLIST_F14(b0_reflist[14].index) | + VDPU_REG_BINIT_RLIST_F13(b0_reflist[13].index) | + VDPU_REG_BINIT_RLIST_F12(b0_reflist[12].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(102)); + + reg = VDPU_REG_BINIT_RLIST_B5(b1_reflist[5].index) | + VDPU_REG_BINIT_RLIST_B4(b1_reflist[4].index) | + VDPU_REG_BINIT_RLIST_B3(b1_reflist[3].index) | + VDPU_REG_BINIT_RLIST_B2(b1_reflist[2].index) | + VDPU_REG_BINIT_RLIST_B1(b1_reflist[1].index) | + VDPU_REG_BINIT_RLIST_B0(b1_reflist[0].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(103)); + + reg = VDPU_REG_BINIT_RLIST_B11(b1_reflist[11].index) | + VDPU_REG_BINIT_RLIST_B10(b1_reflist[10].index) | + VDPU_REG_BINIT_RLIST_B9(b1_reflist[9].index) | + VDPU_REG_BINIT_RLIST_B8(b1_reflist[8].index) | + VDPU_REG_BINIT_RLIST_B7(b1_reflist[7].index) | + VDPU_REG_BINIT_RLIST_B6(b1_reflist[6].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(104)); + + reg = VDPU_REG_BINIT_RLIST_B15(b1_reflist[15].index) | + VDPU_REG_BINIT_RLIST_B14(b1_reflist[14].index) | + VDPU_REG_BINIT_RLIST_B13(b1_reflist[13].index) | + VDPU_REG_BINIT_RLIST_B12(b1_reflist[12].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(105)); + + reg = VDPU_REG_PINIT_RLIST_F3(p_reflist[3].index) | + VDPU_REG_PINIT_RLIST_F2(p_reflist[2].index) | + VDPU_REG_PINIT_RLIST_F1(p_reflist[1].index) | + VDPU_REG_PINIT_RLIST_F0(p_reflist[0].index); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(106)); + + reg = VDPU_REG_REFER_LTERM_E(ctx->h264_dec.dpb_longterm); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(107)); + + reg = VDPU_REG_REFER_VALID_E(ctx->h264_dec.dpb_valid); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(108)); + + /* Set up addresses of DPB buffers. */ + for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { + dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); + + vdpu_write_relaxed(vpu, dma_addr, VDPU_REG_REFER_BASE(i)); + } +} + +static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) +{ + const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; + struct vb2_v4l2_buffer *dst_buf; + struct hantro_dev *vpu = ctx->dev; + dma_addr_t src_dma, dst_dma; + size_t offset = 0; + + /* Source (stream) buffer. */ + src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + vdpu_write_relaxed(vpu, src_dma, VDPU_REG_RLC_VLC_BASE); + + /* Destination (decoded frame) buffer. */ + dst_buf = hantro_get_dst_buf(ctx); + dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); + /* Adjust dma addr to start at second line for bottom field */ + if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) + offset = ALIGN(ctx->src_fmt.width, MB_DIM); + vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DEC_OUT_BASE); + + /* Higher profiles require DMV buffer appended to reference frames. */ + if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { + unsigned int bytes_per_mb = 384; + + /* DMV buffer for monochrome start directly after Y-plane */ + if (ctrls->sps->profile_idc >= 100 && + ctrls->sps->chroma_format_idc == 0) + bytes_per_mb = 256; + offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * + MB_HEIGHT(ctx->src_fmt.height); + + /* + * DMV buffer is split in two for field encoded frames, + * adjust offset for bottom field + */ + if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) + offset += 32 * MB_WIDTH(ctx->src_fmt.width) * + MB_HEIGHT(ctx->src_fmt.height); + vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DIR_MV_BASE); + } + + /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ + vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, VDPU_REG_QTABLE_BASE); +} + +int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf; + u32 reg; + int ret; + + /* Prepare the H264 decoder context. */ + ret = hantro_h264_dec_prepare_run(ctx); + if (ret) + return ret; + + src_buf = hantro_get_src_buf(ctx); + set_params(ctx, src_buf); + set_ref(ctx); + set_buffers(ctx, src_buf); + + hantro_end_prepare_run(ctx); + + /* Start decoding! */ + reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); + vdpu_write(vpu, reg, VDPU_SWREG(57)); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c new file mode 100644 index 000000000000..8395c4d48dd0 --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * + * JPEG encoder + * ------------ + * The VPU JPEG encoder produces JPEG baseline sequential format. + * The quantization coefficients are 8-bit values, complying with + * the baseline specification. Therefore, it requires + * luma and chroma quantization tables. The hardware does entropy + * encoding using internal Huffman tables, as specified in the JPEG + * specification. + * + * In other words, only the luma and chroma quantization tables are + * required for the encoding operation. + * + * Quantization luma table values are written to registers + * VEPU_swreg_0-VEPU_swreg_15, and chroma table values to + * VEPU_swreg_16-VEPU_swreg_31. A special order is needed, neither + * zigzag, nor linear. + */ + +#include +#include +#include "hantro_jpeg.h" +#include "hantro.h" +#include "hantro_v4l2.h" +#include "hantro_hw.h" +#include "rockchip_vpu2_regs.h" + +#define VEPU_JPEG_QUANT_TABLE_COUNT 16 + +static void rockchip_vpu2_set_src_img_ctrl(struct hantro_dev *vpu, + struct hantro_ctx *ctx) +{ + u32 overfill_r, overfill_b; + u32 reg; + + /* + * The format width and height are already macroblock aligned + * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination + * format width and height can be further modified by + * .vidioc_s_selection(), and the width is 4-aligned. + */ + overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; + overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; + + reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width); + vepu_write_relaxed(vpu, reg, VEPU_REG_INPUT_LUMA_INFO); + + reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) | + VEPU_REG_IN_IMG_CTRL_OVRFLB(overfill_b); + /* + * This register controls the input crop, as the offset + * from the right/bottom within the last macroblock. The offset from the + * right must be divided by 4 and so the crop must be aligned to 4 pixels + * horizontally. + */ + vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_OVER_FILL_STRM_OFFSET); + + reg = VEPU_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); + vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_CTRL1); +} + +static void rockchip_vpu2_jpeg_enc_set_buffers(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; + dma_addr_t src[3]; + u32 size_left; + + size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; + if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) + size_left = 0; + + WARN_ON(pix_fmt->num_planes > 3); + + vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + + ctx->vpu_dst_fmt->header_size, + VEPU_REG_ADDR_OUTPUT_STREAM); + vepu_write_relaxed(vpu, size_left, VEPU_REG_STR_BUF_LIMIT); + + if (pix_fmt->num_planes == 1) { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); + } else if (pix_fmt->num_planes == 2) { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); + vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); + vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); + } else { + src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); + src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); + src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); + vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); + vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); + vepu_write_relaxed(vpu, src[2], VEPU_REG_ADDR_IN_PLANE_2); + } +} + +static void +rockchip_vpu2_jpeg_enc_set_qtable(struct hantro_dev *vpu, + unsigned char *luma_qtable, + unsigned char *chroma_qtable) +{ + u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ + for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&luma_qtable_p[i]); + vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); + } + + for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); + vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); + } +} + +int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct hantro_jpeg_ctx jpeg_ctx; + u32 reg; + + src_buf = hantro_get_src_buf(ctx); + dst_buf = hantro_get_dst_buf(ctx); + + hantro_start_prepare_run(ctx); + + memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); + jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + if (!jpeg_ctx.buffer) + return -ENOMEM; + + jpeg_ctx.width = ctx->dst_fmt.width; + jpeg_ctx.height = ctx->dst_fmt.height; + jpeg_ctx.quality = ctx->jpeg_quality; + hantro_jpeg_header_assemble(&jpeg_ctx); + + /* Switch to JPEG encoder mode before writing registers */ + vepu_write_relaxed(vpu, VEPU_REG_ENCODE_FORMAT_JPEG, + VEPU_REG_ENCODE_START); + + rockchip_vpu2_set_src_img_ctrl(vpu, ctx); + rockchip_vpu2_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf); + rockchip_vpu2_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, + jpeg_ctx.hw_chroma_qtable); + + reg = VEPU_REG_OUTPUT_SWAP32 + | VEPU_REG_OUTPUT_SWAP16 + | VEPU_REG_OUTPUT_SWAP8 + | VEPU_REG_INPUT_SWAP8 + | VEPU_REG_INPUT_SWAP16 + | VEPU_REG_INPUT_SWAP32; + /* Make sure that all registers are written at this point. */ + vepu_write(vpu, reg, VEPU_REG_DATA_ENDIAN); + + reg = VEPU_REG_AXI_CTRL_BURST_LEN(16); + vepu_write_relaxed(vpu, reg, VEPU_REG_AXI_CTRL); + + reg = VEPU_REG_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) + | VEPU_REG_MB_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) + | VEPU_REG_FRAME_TYPE_INTRA + | VEPU_REG_ENCODE_FORMAT_JPEG + | VEPU_REG_ENCODE_ENABLE; + + /* Kick the watchdog and start encoding */ + hantro_end_prepare_run(ctx); + vepu_write(vpu, reg, VEPU_REG_ENCODE_START); + + return 0; +} + +void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; + struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + ctx->vpu_dst_fmt->header_size + bytesused); +} diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c new file mode 100644 index 000000000000..b66737fab46b --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include "hantro.h" +#include "hantro_hw.h" + +#define VDPU_SWREG(nr) ((nr) * 4) + +#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) +#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) +#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) +#define VDPU_REG_REFER0_BASE VDPU_SWREG(131) +#define VDPU_REG_REFER2_BASE VDPU_SWREG(134) +#define VDPU_REG_REFER3_BASE VDPU_SWREG(135) +#define VDPU_REG_REFER1_BASE VDPU_SWREG(148) +#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) +#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) +#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) +#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) + +#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) +#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) + +#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) +#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) +#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) + +#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) + +#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) +#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) +#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) +#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) +#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) +#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) +#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) +#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) + +#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) +#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) +#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) +#define VDPU_REG_PIC_B_E(v) ((v) ? BIT(15) : 0) +#define VDPU_REG_PIC_INTER_E(v) ((v) ? BIT(14) : 0) +#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) +#define VDPU_REG_FWD_INTERLACE_E(v) ((v) ? BIT(12) : 0) +#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) +#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) + +#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) +#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) +#define VDPU_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) +#define VDPU_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) + +#define VDPU_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) +#define VDPU_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) +#define VDPU_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) +#define VDPU_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) +#define VDPU_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) +#define VDPU_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) +#define VDPU_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) +#define VDPU_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) +#define VDPU_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) +#define VDPU_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) +#define VDPU_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) +#define VDPU_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) + +static void +rockchip_vpu2_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, + struct hantro_ctx *ctx) +{ + struct v4l2_ctrl_mpeg2_quantisation *q; + + q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); + hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); + vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, VDPU_REG_QTABLE_BASE); +} + +static void +rockchip_vpu2_mpeg2_dec_set_buffers(struct hantro_dev *vpu, + struct hantro_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf, + const struct v4l2_ctrl_mpeg2_sequence *seq, + const struct v4l2_ctrl_mpeg2_picture *pic) +{ + dma_addr_t forward_addr = 0, backward_addr = 0; + dma_addr_t current_addr, addr; + + switch (pic->picture_coding_type) { + case V4L2_MPEG2_PIC_CODING_TYPE_B: + backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); + fallthrough; + case V4L2_MPEG2_PIC_CODING_TYPE_P: + forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); + } + + /* Source bitstream buffer */ + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + vdpu_write_relaxed(vpu, addr, VDPU_REG_RLC_VLC_BASE); + + /* Destination frame buffer */ + addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + current_addr = addr; + + if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) + addr += ALIGN(ctx->dst_fmt.width, 16); + vdpu_write_relaxed(vpu, addr, VDPU_REG_DEC_OUT_BASE); + + if (!forward_addr) + forward_addr = current_addr; + if (!backward_addr) + backward_addr = current_addr; + + /* Set forward ref frame (top/bottom field) */ + if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || + pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || + (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && + pic->flags & V4L2_MPEG2_PIC_TOP_FIELD) || + (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && + !(pic->flags & V4L2_MPEG2_PIC_TOP_FIELD))) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); + } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER1_BASE); + } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { + vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); + } + + /* Set backward ref frame (top/bottom field) */ + vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER2_BASE); + vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER3_BASE); +} + +int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct v4l2_ctrl_mpeg2_sequence *seq; + const struct v4l2_ctrl_mpeg2_picture *pic; + u32 reg; + + src_buf = hantro_get_src_buf(ctx); + dst_buf = hantro_get_dst_buf(ctx); + + hantro_start_prepare_run(ctx); + + seq = hantro_get_ctrl(ctx, + V4L2_CID_STATELESS_MPEG2_SEQUENCE); + pic = hantro_get_ctrl(ctx, + V4L2_CID_STATELESS_MPEG2_PICTURE); + + reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | + VDPU_REG_DEC_SCMD_DIS(0) | + VDPU_REG_FILTERING_DIS(1) | + VDPU_REG_DEC_LATENCY(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); + + reg = VDPU_REG_INIT_QP(1) | + VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); + + reg = VDPU_REG_APF_THRESHOLD(8) | + VDPU_REG_STARTMB_X(0) | + VDPU_REG_STARTMB_Y(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); + + reg = VDPU_REG_DEC_MODE(5); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); + + reg = VDPU_REG_DEC_STRENDIAN_E(1) | + VDPU_REG_DEC_STRSWAP32_E(1) | + VDPU_REG_DEC_OUTSWAP32_E(1) | + VDPU_REG_DEC_INSWAP32_E(1) | + VDPU_REG_DEC_OUT_ENDIAN(1) | + VDPU_REG_DEC_IN_ENDIAN(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); + + reg = VDPU_REG_DEC_DATA_DISC_E(0) | + VDPU_REG_DEC_MAX_BURST(16) | + VDPU_REG_DEC_AXI_WR_ID(0) | + VDPU_REG_DEC_AXI_RD_ID(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); + + reg = VDPU_REG_RLC_MODE_E(0) | + VDPU_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | + VDPU_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | + VDPU_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | + VDPU_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | + VDPU_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | + VDPU_REG_FWD_INTERLACE_E(0) | + VDPU_REG_WRITE_MVS_E(0) | + VDPU_REG_DEC_TIMEOUT_E(1) | + VDPU_REG_DEC_CLK_GATE_E(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); + + reg = VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | + VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | + VDPU_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | + VDPU_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(120)); + + reg = VDPU_REG_STRM_START_BIT(0) | + VDPU_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | + VDPU_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | + VDPU_REG_INTRA_DC_PREC(pic->intra_dc_precision) | + VDPU_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | + VDPU_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(122)); + + reg = VDPU_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | + VDPU_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | + VDPU_REG_FCODE_FWD_VER(pic->f_code[0][1]) | + VDPU_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | + VDPU_REG_FCODE_BWD_VER(pic->f_code[1][1]) | + VDPU_REG_MV_ACCURACY_FWD(1) | + VDPU_REG_MV_ACCURACY_BWD(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(136)); + + rockchip_vpu2_mpeg2_dec_set_quantisation(vpu, ctx); + + rockchip_vpu2_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf, seq, pic); + + /* Kick the watchdog and start decoding */ + hantro_end_prepare_run(ctx); + + reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); + vdpu_write(vpu, reg, VDPU_SWREG(57)); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c new file mode 100644 index 000000000000..d079075448c9 --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VPU codec vp8 decode driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * ZhiChao Yu + * + * Copyright (C) 2014 Google LLC. + * Tomasz Figa + * + * Copyright (C) 2015 Rockchip Electronics Co., Ltd. + * Alpha Lin + */ + +#include + +#include "hantro_hw.h" +#include "hantro.h" +#include "hantro_g1_regs.h" + +#define VDPU_REG_DEC_CTRL0 0x0c8 +#define VDPU_REG_STREAM_LEN 0x0cc +#define VDPU_REG_DEC_FORMAT 0x0d4 +#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) +#define VDPU_REG_DATA_ENDIAN 0x0d8 +#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) +#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) +#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) +#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) +#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) +#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) +#define VDPU_REG_AXI_CTRL 0x0e0 +#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) +#define VDPU_REG_EN_FLAGS 0x0e4 +#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) +#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) +#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) +#define VDPU_REG_PRED_FLT 0x0ec +#define VDPU_REG_ADDR_QTABLE 0x0f4 +#define VDPU_REG_ADDR_DST 0x0fc +#define VDPU_REG_ADDR_STR 0x100 +#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 +#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 +#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) +#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) +#define VDPU_REG_VP8_CTRL0 0x1e8 +#define VDPU_REG_VP8_DATA_VAL 0x1f0 +#define VDPU_REG_PRED_FLT7 0x1f4 +#define VDPU_REG_PRED_FLT8 0x1f8 +#define VDPU_REG_PRED_FLT9 0x1fc +#define VDPU_REG_PRED_FLT10 0x200 +#define VDPU_REG_FILTER_LEVEL 0x204 +#define VDPU_REG_VP8_QUANTER0 0x208 +#define VDPU_REG_VP8_ADDR_REF0 0x20c +#define VDPU_REG_FILTER_MB_ADJ 0x210 +#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) +#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) +#define VDPU_REG_FILTER_REF_ADJ 0x214 +#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) +#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) +#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) +#define VDPU_REG_VP8_DCT_BASE(i) \ + (0x230 + ((((i) < 5) ? (i) : ((i) + 1)) * 0x4)) +#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 +#define VDPU_REG_VP8_SEGMENT_VAL 0x254 +#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) +#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) +#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) +#define VDPU_REG_VP8_DCT_START_BIT2 0x258 +#define VDPU_REG_VP8_QUANTER1 0x25c +#define VDPU_REG_VP8_QUANTER2 0x260 +#define VDPU_REG_PRED_FLT1 0x264 +#define VDPU_REG_PRED_FLT2 0x268 +#define VDPU_REG_PRED_FLT3 0x26c +#define VDPU_REG_PRED_FLT4 0x270 +#define VDPU_REG_PRED_FLT5 0x274 +#define VDPU_REG_PRED_FLT6 0x278 + +static const struct hantro_reg vp8_dec_dct_base[8] = { + { VDPU_REG_ADDR_STR, 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(0), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(1), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(2), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(3), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(4), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(5), 0, 0xffffffff }, + { VDPU_REG_VP8_DCT_BASE(6), 0, 0xffffffff }, +}; + +static const struct hantro_reg vp8_dec_lf_level[4] = { + { VDPU_REG_FILTER_LEVEL, 18, 0x3f }, + { VDPU_REG_FILTER_LEVEL, 12, 0x3f }, + { VDPU_REG_FILTER_LEVEL, 6, 0x3f }, + { VDPU_REG_FILTER_LEVEL, 0, 0x3f }, +}; + +static const struct hantro_reg vp8_dec_mb_adj[4] = { + { VDPU_REG_FILTER_MB_ADJ, 21, 0x7f }, + { VDPU_REG_FILTER_MB_ADJ, 14, 0x7f }, + { VDPU_REG_FILTER_MB_ADJ, 7, 0x7f }, + { VDPU_REG_FILTER_MB_ADJ, 0, 0x7f }, +}; + +static const struct hantro_reg vp8_dec_ref_adj[4] = { + { VDPU_REG_FILTER_REF_ADJ, 21, 0x7f }, + { VDPU_REG_FILTER_REF_ADJ, 14, 0x7f }, + { VDPU_REG_FILTER_REF_ADJ, 7, 0x7f }, + { VDPU_REG_FILTER_REF_ADJ, 0, 0x7f }, +}; + +static const struct hantro_reg vp8_dec_quant[4] = { + { VDPU_REG_VP8_QUANTER0, 11, 0x7ff }, + { VDPU_REG_VP8_QUANTER0, 0, 0x7ff }, + { VDPU_REG_VP8_QUANTER1, 11, 0x7ff }, + { VDPU_REG_VP8_QUANTER1, 0, 0x7ff }, +}; + +static const struct hantro_reg vp8_dec_quant_delta[5] = { + { VDPU_REG_VP8_QUANTER0, 27, 0x1f }, + { VDPU_REG_VP8_QUANTER0, 22, 0x1f }, + { VDPU_REG_VP8_QUANTER1, 27, 0x1f }, + { VDPU_REG_VP8_QUANTER1, 22, 0x1f }, + { VDPU_REG_VP8_QUANTER2, 27, 0x1f }, +}; + +static const struct hantro_reg vp8_dec_dct_start_bits[8] = { + { VDPU_REG_VP8_CTRL0, 26, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT, 26, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT, 20, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT2, 24, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT2, 18, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT2, 12, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT2, 6, 0x3f }, + { VDPU_REG_VP8_DCT_START_BIT2, 0, 0x3f }, +}; + +static const struct hantro_reg vp8_dec_pred_bc_tap[8][6] = { + { + { 0, 0, 0}, + { VDPU_REG_PRED_FLT, 22, 0x3ff }, + { VDPU_REG_PRED_FLT, 12, 0x3ff }, + { VDPU_REG_PRED_FLT, 2, 0x3ff }, + { VDPU_REG_PRED_FLT1, 22, 0x3ff }, + { 0, 0, 0}, + }, { + { 0, 0, 0}, + { VDPU_REG_PRED_FLT1, 12, 0x3ff }, + { VDPU_REG_PRED_FLT1, 2, 0x3ff }, + { VDPU_REG_PRED_FLT2, 22, 0x3ff }, + { VDPU_REG_PRED_FLT2, 12, 0x3ff }, + { 0, 0, 0}, + }, { + { VDPU_REG_PRED_FLT10, 10, 0x3 }, + { VDPU_REG_PRED_FLT2, 2, 0x3ff }, + { VDPU_REG_PRED_FLT3, 22, 0x3ff }, + { VDPU_REG_PRED_FLT3, 12, 0x3ff }, + { VDPU_REG_PRED_FLT3, 2, 0x3ff }, + { VDPU_REG_PRED_FLT10, 8, 0x3}, + }, { + { 0, 0, 0}, + { VDPU_REG_PRED_FLT4, 22, 0x3ff }, + { VDPU_REG_PRED_FLT4, 12, 0x3ff }, + { VDPU_REG_PRED_FLT4, 2, 0x3ff }, + { VDPU_REG_PRED_FLT5, 22, 0x3ff }, + { 0, 0, 0}, + }, { + { VDPU_REG_PRED_FLT10, 6, 0x3 }, + { VDPU_REG_PRED_FLT5, 12, 0x3ff }, + { VDPU_REG_PRED_FLT5, 2, 0x3ff }, + { VDPU_REG_PRED_FLT6, 22, 0x3ff }, + { VDPU_REG_PRED_FLT6, 12, 0x3ff }, + { VDPU_REG_PRED_FLT10, 4, 0x3 }, + }, { + { 0, 0, 0}, + { VDPU_REG_PRED_FLT6, 2, 0x3ff }, + { VDPU_REG_PRED_FLT7, 22, 0x3ff }, + { VDPU_REG_PRED_FLT7, 12, 0x3ff }, + { VDPU_REG_PRED_FLT7, 2, 0x3ff }, + { 0, 0, 0}, + }, { + { VDPU_REG_PRED_FLT10, 2, 0x3 }, + { VDPU_REG_PRED_FLT8, 22, 0x3ff }, + { VDPU_REG_PRED_FLT8, 12, 0x3ff }, + { VDPU_REG_PRED_FLT8, 2, 0x3ff }, + { VDPU_REG_PRED_FLT9, 22, 0x3ff }, + { VDPU_REG_PRED_FLT10, 0, 0x3 }, + }, { + { 0, 0, 0}, + { VDPU_REG_PRED_FLT9, 12, 0x3ff }, + { VDPU_REG_PRED_FLT9, 2, 0x3ff }, + { VDPU_REG_PRED_FLT10, 22, 0x3ff }, + { VDPU_REG_PRED_FLT10, 12, 0x3ff }, + { 0, 0, 0}, + }, +}; + +static const struct hantro_reg vp8_dec_mb_start_bit = { + .base = VDPU_REG_VP8_CTRL0, + .shift = 18, + .mask = 0x3f +}; + +static const struct hantro_reg vp8_dec_mb_aligned_data_len = { + .base = VDPU_REG_VP8_DATA_VAL, + .shift = 0, + .mask = 0x3fffff +}; + +static const struct hantro_reg vp8_dec_num_dct_partitions = { + .base = VDPU_REG_VP8_DATA_VAL, + .shift = 24, + .mask = 0xf +}; + +static const struct hantro_reg vp8_dec_stream_len = { + .base = VDPU_REG_STREAM_LEN, + .shift = 0, + .mask = 0xffffff +}; + +static const struct hantro_reg vp8_dec_mb_width = { + .base = VDPU_REG_VP8_PIC_MB_SIZE, + .shift = 23, + .mask = 0x1ff +}; + +static const struct hantro_reg vp8_dec_mb_height = { + .base = VDPU_REG_VP8_PIC_MB_SIZE, + .shift = 11, + .mask = 0xff +}; + +static const struct hantro_reg vp8_dec_mb_width_ext = { + .base = VDPU_REG_VP8_PIC_MB_SIZE, + .shift = 3, + .mask = 0x7 +}; + +static const struct hantro_reg vp8_dec_mb_height_ext = { + .base = VDPU_REG_VP8_PIC_MB_SIZE, + .shift = 0, + .mask = 0x7 +}; + +static const struct hantro_reg vp8_dec_bool_range = { + .base = VDPU_REG_VP8_CTRL0, + .shift = 0, + .mask = 0xff +}; + +static const struct hantro_reg vp8_dec_bool_value = { + .base = VDPU_REG_VP8_CTRL0, + .shift = 8, + .mask = 0xff +}; + +static const struct hantro_reg vp8_dec_filter_disable = { + .base = VDPU_REG_DEC_CTRL0, + .shift = 8, + .mask = 1 +}; + +static const struct hantro_reg vp8_dec_skip_mode = { + .base = VDPU_REG_DEC_CTRL0, + .shift = 9, + .mask = 1 +}; + +static const struct hantro_reg vp8_dec_start_dec = { + .base = VDPU_REG_EN_FLAGS, + .shift = 0, + .mask = 1 +}; + +static void cfg_lf(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + const struct v4l2_vp8_segment *seg = &hdr->segment; + const struct v4l2_vp8_loop_filter *lf = &hdr->lf; + struct hantro_dev *vpu = ctx->dev; + unsigned int i; + u32 reg; + + if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { + hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); + } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { + for (i = 0; i < 4; i++) { + u32 lf_level = clamp(lf->level + seg->lf_update[i], + 0, 63); + + hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); + } + } else { + for (i = 0; i < 4; i++) + hantro_reg_write(vpu, &vp8_dec_lf_level[i], + seg->lf_update[i]); + } + + reg = VDPU_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); + if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) + reg |= VDPU_REG_REF_PIC_FILT_TYPE_E; + vdpu_write_relaxed(vpu, reg, VDPU_REG_FILTER_MB_ADJ); + + if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { + for (i = 0; i < 4; i++) { + hantro_reg_write(vpu, &vp8_dec_mb_adj[i], + lf->mb_mode_delta[i]); + hantro_reg_write(vpu, &vp8_dec_ref_adj[i], + lf->ref_frm_delta[i]); + } + } +} + +static void cfg_qp(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + const struct v4l2_vp8_quantization *q = &hdr->quant; + const struct v4l2_vp8_segment *seg = &hdr->segment; + struct hantro_dev *vpu = ctx->dev; + unsigned int i; + + if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { + hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); + } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { + for (i = 0; i < 4; i++) { + u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], + 0, 127); + + hantro_reg_write(vpu, &vp8_dec_quant[i], quant); + } + } else { + for (i = 0; i < 4; i++) + hantro_reg_write(vpu, &vp8_dec_quant[i], + seg->quant_update[i]); + } + + hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); + hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); +} + +static void cfg_parts(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *vb2_src; + u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; + u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; + u32 dct_size_part_size, dct_part_offset; + dma_addr_t src_dma; + u32 dct_part_total_len = 0; + u32 count = 0; + unsigned int i; + + vb2_src = hantro_get_src_buf(ctx); + src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); + + /* + * Calculate control partition mb data info + * @first_part_header_bits: bits offset of mb data from first + * part start pos + * @mb_offset_bits: bits offset of mb data from src_dma + * base addr + * @mb_offset_byte: bytes offset of mb data from src_dma + * base addr + * @mb_start_bits: bits offset of mb data from mb data + * 64bits alignment addr + */ + mb_offset_bits = first_part_offset * 8 + + hdr->first_part_header_bits + 8; + mb_offset_bytes = mb_offset_bits / 8; + mb_start_bits = mb_offset_bits - + (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; + mb_size = hdr->first_part_size - + (mb_offset_bytes - first_part_offset) + + (mb_offset_bytes & DEC_8190_ALIGN_MASK); + + /* Macroblock data aligned base addr */ + vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) + + src_dma, VDPU_REG_VP8_ADDR_CTRL_PART); + hantro_reg_write(vpu, &vp8_dec_mb_start_bit, mb_start_bits); + hantro_reg_write(vpu, &vp8_dec_mb_aligned_data_len, mb_size); + + /* + * Calculate DCT partition info + * @dct_size_part_size: Containing sizes of DCT part, every DCT part + * has 3 bytes to store its size, except the last + * DCT part + * @dct_part_offset: bytes offset of DCT parts from src_dma base addr + * @dct_part_total_len: total size of all DCT parts + */ + dct_size_part_size = (hdr->num_dct_parts - 1) * 3; + dct_part_offset = first_part_offset + hdr->first_part_size; + for (i = 0; i < hdr->num_dct_parts; i++) + dct_part_total_len += hdr->dct_part_sizes[i]; + dct_part_total_len += dct_size_part_size; + dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); + + /* Number of DCT partitions */ + hantro_reg_write(vpu, &vp8_dec_num_dct_partitions, + hdr->num_dct_parts - 1); + + /* DCT partition length */ + hantro_reg_write(vpu, &vp8_dec_stream_len, dct_part_total_len); + + /* DCT partitions base address */ + for (i = 0; i < hdr->num_dct_parts; i++) { + u32 byte_offset = dct_part_offset + dct_size_part_size + count; + u32 base_addr = byte_offset + src_dma; + + hantro_reg_write(vpu, &vp8_dec_dct_base[i], + base_addr & (~DEC_8190_ALIGN_MASK)); + + hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], + (byte_offset & DEC_8190_ALIGN_MASK) * 8); + + count += hdr->dct_part_sizes[i]; + } +} + +/* + * prediction filter taps + * normal 6-tap filters + */ +static void cfg_tap(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr) +{ + struct hantro_dev *vpu = ctx->dev; + int i, j; + + if ((hdr->version & 0x03) != 0) + return; /* Tap filter not used. */ + + for (i = 0; i < 8; i++) { + for (j = 0; j < 6; j++) { + if (vp8_dec_pred_bc_tap[i][j].base != 0) + hantro_reg_write(vpu, + &vp8_dec_pred_bc_tap[i][j], + hantro_vp8_dec_mc_filter[i][j]); + } + } +} + +static void cfg_ref(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr, + struct vb2_v4l2_buffer *vb2_dst) +{ + struct hantro_dev *vpu = ctx->dev; + dma_addr_t ref; + + ref = hantro_get_ref(ctx, hdr->last_frame_ts); + if (!ref) { + vpu_debug(0, "failed to find last frame ts=%llu\n", + hdr->last_frame_ts); + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + } + vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF0); + + ref = hantro_get_ref(ctx, hdr->golden_frame_ts); + if (!ref && hdr->golden_frame_ts) + vpu_debug(0, "failed to find golden frame ts=%llu\n", + hdr->golden_frame_ts); + if (!ref) + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) + ref |= VDPU_REG_VP8_GREF_SIGN_BIAS; + vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(2)); + + ref = hantro_get_ref(ctx, hdr->alt_frame_ts); + if (!ref && hdr->alt_frame_ts) + vpu_debug(0, "failed to find alt frame ts=%llu\n", + hdr->alt_frame_ts); + if (!ref) + ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) + ref |= VDPU_REG_VP8_AREF_SIGN_BIAS; + vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(3)); +} + +static void cfg_buffers(struct hantro_ctx *ctx, + const struct v4l2_ctrl_vp8_frame *hdr, + struct vb2_v4l2_buffer *vb2_dst) +{ + const struct v4l2_vp8_segment *seg = &hdr->segment; + struct hantro_dev *vpu = ctx->dev; + dma_addr_t dst_dma; + u32 reg; + + /* Set probability table buffer address */ + vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, + VDPU_REG_ADDR_QTABLE); + + /* Set segment map address */ + reg = VDPU_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); + if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { + reg |= VDPU_REG_FWD_PIC1_SEGMENT_E; + if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) + reg |= VDPU_REG_FWD_PIC1_SEGMENT_UPD_E; + } + vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_SEGMENT_VAL); + + /* set output frame buffer address */ + dst_dma = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); + vdpu_write_relaxed(vpu, dst_dma, VDPU_REG_ADDR_DST); +} + +int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx) +{ + const struct v4l2_ctrl_vp8_frame *hdr; + struct hantro_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *vb2_dst; + size_t height = ctx->dst_fmt.height; + size_t width = ctx->dst_fmt.width; + u32 mb_width, mb_height; + u32 reg; + + hantro_start_prepare_run(ctx); + + hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); + if (WARN_ON(!hdr)) + return -EINVAL; + + /* Reset segment_map buffer in keyframe */ + if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) + memset(ctx->vp8_dec.segment_map.cpu, 0, + ctx->vp8_dec.segment_map.size); + + hantro_vp8_prob_update(ctx, hdr); + + /* + * Extensive testing shows that the hardware does not properly + * clear the internal state from previous a decoding run. This + * causes corruption in decoded frames for multi-instance use cases. + * A soft reset before programming the registers has been found + * to resolve those problems. + */ + ctx->codec_ops->reset(ctx); + + reg = VDPU_REG_CONFIG_DEC_TIMEOUT_E + | VDPU_REG_CONFIG_DEC_CLK_GATE_E; + if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) + reg |= VDPU_REG_DEC_CTRL0_PIC_INTER_E; + vdpu_write_relaxed(vpu, reg, VDPU_REG_EN_FLAGS); + + reg = VDPU_REG_CONFIG_DEC_STRENDIAN_E + | VDPU_REG_CONFIG_DEC_INSWAP32_E + | VDPU_REG_CONFIG_DEC_STRSWAP32_E + | VDPU_REG_CONFIG_DEC_OUTSWAP32_E + | VDPU_REG_CONFIG_DEC_IN_ENDIAN + | VDPU_REG_CONFIG_DEC_OUT_ENDIAN; + vdpu_write_relaxed(vpu, reg, VDPU_REG_DATA_ENDIAN); + + reg = VDPU_REG_CONFIG_DEC_MAX_BURST(16); + vdpu_write_relaxed(vpu, reg, VDPU_REG_AXI_CTRL); + + reg = VDPU_REG_DEC_CTRL0_DEC_MODE(10); + vdpu_write_relaxed(vpu, reg, VDPU_REG_DEC_FORMAT); + + if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) + hantro_reg_write(vpu, &vp8_dec_skip_mode, 1); + if (hdr->lf.level == 0) + hantro_reg_write(vpu, &vp8_dec_filter_disable, 1); + + /* Frame dimensions */ + mb_width = MB_WIDTH(width); + mb_height = MB_HEIGHT(height); + + hantro_reg_write(vpu, &vp8_dec_mb_width, mb_width); + hantro_reg_write(vpu, &vp8_dec_mb_height, mb_height); + hantro_reg_write(vpu, &vp8_dec_mb_width_ext, mb_width >> 9); + hantro_reg_write(vpu, &vp8_dec_mb_height_ext, mb_height >> 8); + + /* Boolean decoder */ + hantro_reg_write(vpu, &vp8_dec_bool_range, hdr->coder_state.range); + hantro_reg_write(vpu, &vp8_dec_bool_value, hdr->coder_state.value); + + reg = vdpu_read(vpu, VDPU_REG_VP8_DCT_START_BIT); + if (hdr->version != 3) + reg |= VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT; + if (hdr->version & 0x3) + reg |= VDPU_REG_DEC_CTRL4_BILIN_MC_E; + vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_DCT_START_BIT); + + cfg_lf(ctx, hdr); + cfg_qp(ctx, hdr); + cfg_parts(ctx, hdr); + cfg_tap(ctx, hdr); + + vb2_dst = hantro_get_dst_buf(ctx); + cfg_ref(ctx, hdr, vb2_dst); + cfg_buffers(ctx, hdr, vb2_dst); + + hantro_end_prepare_run(ctx); + + hantro_reg_write(vpu, &vp8_dec_start_dec, 1); + + return 0; +} diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_regs.h b/drivers/media/platform/verisilicon/rockchip_vpu2_regs.h new file mode 100644 index 000000000000..49e40889545b --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu2_regs.h @@ -0,0 +1,600 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Alpha Lin + */ + +#ifndef ROCKCHIP_VPU2_REGS_H_ +#define ROCKCHIP_VPU2_REGS_H_ + +/* Encoder registers. */ +#define VEPU_REG_VP8_QUT_1ST(i) (0x000 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_DC_Y2(x) (((x) & 0x3fff) << 16) +#define VEPU_REG_VP8_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_QUT_2ND(i) (0x004 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_AC_Y1(x) (((x) & 0x3fff) << 16) +#define VEPU_REG_VP8_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_QUT_3RD(i) (0x008 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_AC_CHR(x) (((x) & 0x3fff) << 16) +#define VEPU_REG_VP8_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_QUT_4TH(i) (0x00c + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_ZB_DC_CHR(x) (((x) & 0x1ff) << 18) +#define VEPU_REG_VP8_QUT_ZB_DC_Y2(x) (((x) & 0x1ff) << 9) +#define VEPU_REG_VP8_QUT_ZB_DC_Y1(x) (((x) & 0x1ff) << 0) +#define VEPU_REG_VP8_QUT_5TH(i) (0x010 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_ZB_AC_CHR(x) (((x) & 0x1ff) << 18) +#define VEPU_REG_VP8_QUT_ZB_AC_Y2(x) (((x) & 0x1ff) << 9) +#define VEPU_REG_VP8_QUT_ZB_AC_Y1(x) (((x) & 0x1ff) << 0) +#define VEPU_REG_VP8_QUT_6TH(i) (0x014 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_RND_DC_CHR(x) (((x) & 0xff) << 16) +#define VEPU_REG_VP8_QUT_RND_DC_Y2(x) (((x) & 0xff) << 8) +#define VEPU_REG_VP8_QUT_RND_DC_Y1(x) (((x) & 0xff) << 0) +#define VEPU_REG_VP8_QUT_7TH(i) (0x018 + ((i) * 0x24)) +#define VEPU_REG_VP8_QUT_RND_AC_CHR(x) (((x) & 0xff) << 16) +#define VEPU_REG_VP8_QUT_RND_AC_Y2(x) (((x) & 0xff) << 8) +#define VEPU_REG_VP8_QUT_RND_AC_Y1(x) (((x) & 0xff) << 0) +#define VEPU_REG_VP8_QUT_8TH(i) (0x01c + ((i) * 0x24)) +#define VEPU_REG_VP8_SEG_FILTER_LEVEL(x) (((x) & 0x3f) << 25) +#define VEPU_REG_VP8_DEQUT_DC_CHR(x) (((x) & 0xff) << 17) +#define VEPU_REG_VP8_DEQUT_DC_Y2(x) (((x) & 0x1ff) << 8) +#define VEPU_REG_VP8_DEQUT_DC_Y1(x) (((x) & 0xff) << 0) +#define VEPU_REG_VP8_QUT_9TH(i) (0x020 + ((i) * 0x24)) +#define VEPU_REG_VP8_DEQUT_AC_CHR(x) (((x) & 0x1ff) << 18) +#define VEPU_REG_VP8_DEQUT_AC_Y2(x) (((x) & 0x1ff) << 9) +#define VEPU_REG_VP8_DEQUT_AC_Y1(x) (((x) & 0x1ff) << 0) +#define VEPU_REG_ADDR_VP8_SEG_MAP 0x06c +#define VEPU_REG_VP8_INTRA_4X4_PENALTY(i) (0x070 + ((i) * 0x4)) +#define VEPU_REG_VP8_INTRA_4X4_PENALTY_0(x) (((x) & 0xfff) << 0) +#define VEPU_REG_VP8_INTRA_4x4_PENALTY_1(x) (((x) & 0xfff) << 16) +#define VEPU_REG_VP8_INTRA_16X16_PENALTY(i) (0x084 + ((i) * 0x4)) +#define VEPU_REG_VP8_INTRA_16X16_PENALTY_0(x) (((x) & 0xfff) << 0) +#define VEPU_REG_VP8_INTRA_16X16_PENALTY_1(x) (((x) & 0xfff) << 16) +#define VEPU_REG_VP8_CONTROL 0x0a0 +#define VEPU_REG_VP8_LF_MODE_DELTA_BPRED(x) (((x) & 0x1f) << 24) +#define VEPU_REG_VP8_LF_REF_DELTA_INTRA_MB(x) (((x) & 0x7f) << 16) +#define VEPU_REG_VP8_INTER_TYPE_BIT_COST(x) (((x) & 0xfff) << 0) +#define VEPU_REG_VP8_REF_FRAME_VAL 0x0a4 +#define VEPU_REG_VP8_COEF_DMV_PENALTY(x) (((x) & 0xfff) << 16) +#define VEPU_REG_VP8_REF_FRAME(x) (((x) & 0xfff) << 0) +#define VEPU_REG_VP8_LOOP_FILTER_REF_DELTA 0x0a8 +#define VEPU_REG_VP8_LF_REF_DELTA_ALT_REF(x) (((x) & 0x7f) << 16) +#define VEPU_REG_VP8_LF_REF_DELTA_LAST_REF(x) (((x) & 0x7f) << 8) +#define VEPU_REG_VP8_LF_REF_DELTA_GOLDEN(x) (((x) & 0x7f) << 0) +#define VEPU_REG_VP8_LOOP_FILTER_MODE_DELTA 0x0ac +#define VEPU_REG_VP8_LF_MODE_DELTA_SPLITMV(x) (((x) & 0x7f) << 16) +#define VEPU_REG_VP8_LF_MODE_DELTA_ZEROMV(x) (((x) & 0x7f) << 8) +#define VEPU_REG_VP8_LF_MODE_DELTA_NEWMV(x) (((x) & 0x7f) << 0) +#define VEPU_REG_JPEG_LUMA_QUAT(i) (0x000 + ((i) * 0x4)) +#define VEPU_REG_JPEG_CHROMA_QUAT(i) (0x040 + ((i) * 0x4)) +#define VEPU_REG_INTRA_SLICE_BITMAP(i) (0x0b0 + ((i) * 0x4)) +#define VEPU_REG_ADDR_VP8_DCT_PART(i) (0x0b0 + ((i) * 0x4)) +#define VEPU_REG_INTRA_AREA_CTRL 0x0b8 +#define VEPU_REG_INTRA_AREA_TOP(x) (((x) & 0xff) << 24) +#define VEPU_REG_INTRA_AREA_BOTTOM(x) (((x) & 0xff) << 16) +#define VEPU_REG_INTRA_AREA_LEFT(x) (((x) & 0xff) << 8) +#define VEPU_REG_INTRA_AREA_RIGHT(x) (((x) & 0xff) << 0) +#define VEPU_REG_CIR_INTRA_CTRL 0x0bc +#define VEPU_REG_CIR_INTRA_FIRST_MB(x) (((x) & 0xffff) << 16) +#define VEPU_REG_CIR_INTRA_INTERVAL(x) (((x) & 0xffff) << 0) +#define VEPU_REG_ADDR_IN_PLANE_0 0x0c0 +#define VEPU_REG_ADDR_IN_PLANE_1 0x0c4 +#define VEPU_REG_ADDR_IN_PLANE_2 0x0c8 +#define VEPU_REG_STR_HDR_REM_MSB 0x0cc +#define VEPU_REG_STR_HDR_REM_LSB 0x0d0 +#define VEPU_REG_STR_BUF_LIMIT 0x0d4 +#define VEPU_REG_AXI_CTRL 0x0d8 +#define VEPU_REG_AXI_CTRL_READ_ID(x) (((x) & 0xff) << 24) +#define VEPU_REG_AXI_CTRL_WRITE_ID(x) (((x) & 0xff) << 16) +#define VEPU_REG_AXI_CTRL_BURST_LEN(x) (((x) & 0x3f) << 8) +#define VEPU_REG_AXI_CTRL_INCREMENT_MODE(x) (((x) & 0x01) << 2) +#define VEPU_REG_AXI_CTRL_BIRST_DISCARD(x) (((x) & 0x01) << 1) +#define VEPU_REG_AXI_CTRL_BIRST_DISABLE BIT(0) +#define VEPU_QP_ADJUST_MAD_DELTA_ROI 0x0dc +#define VEPU_REG_ROI_QP_DELTA_1 (((x) & 0xf) << 12) +#define VEPU_REG_ROI_QP_DELTA_2 (((x) & 0xf) << 8) +#define VEPU_REG_MAD_QP_ADJUSTMENT (((x) & 0xf) << 0) +#define VEPU_REG_ADDR_REF_LUMA 0x0e0 +#define VEPU_REG_ADDR_REF_CHROMA 0x0e4 +#define VEPU_REG_QP_SUM_DIV2 0x0e8 +#define VEPU_REG_QP_SUM(x) (((x) & 0x001fffff) * 2) +#define VEPU_REG_ENC_CTRL0 0x0ec +#define VEPU_REG_DISABLE_QUARTER_PIXEL_MV BIT(28) +#define VEPU_REG_DEBLOCKING_FILTER_MODE(x) (((x) & 0x3) << 24) +#define VEPU_REG_CABAC_INIT_IDC(x) (((x) & 0x3) << 21) +#define VEPU_REG_ENTROPY_CODING_MODE BIT(20) +#define VEPU_REG_H264_TRANS8X8_MODE BIT(17) +#define VEPU_REG_H264_INTER4X4_MODE BIT(16) +#define VEPU_REG_H264_STREAM_MODE BIT(15) +#define VEPU_REG_H264_SLICE_SIZE(x) (((x) & 0x7f) << 8) +#define VEPU_REG_ENC_OVER_FILL_STRM_OFFSET 0x0f0 +#define VEPU_REG_STREAM_START_OFFSET(x) (((x) & 0x3f) << 16) +#define VEPU_REG_SKIP_MACROBLOCK_PENALTY(x) (((x) & 0xff) << 8) +#define VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(x) (((x) & 0x3) << 4) +#define VEPU_REG_IN_IMG_CTRL_OVRFLB(x) (((x) & 0xf) << 0) +#define VEPU_REG_INPUT_LUMA_INFO 0x0f4 +#define VEPU_REG_IN_IMG_CHROMA_OFFSET(x) (((x) & 0x7) << 20) +#define VEPU_REG_IN_IMG_LUMA_OFFSET(x) (((x) & 0x7) << 16) +#define VEPU_REG_IN_IMG_CTRL_ROW_LEN(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_RLC_SUM 0x0f8 +#define VEPU_REG_RLC_SUM_OUT(x) (((x) & 0x007fffff) * 4) +#define VEPU_REG_SPLIT_PENALTY_4X4 0x0f8 +#define VEPU_REG_VP8_SPLIT_PENALTY_4X4 (((x) & 0x1ff) << 19) +#define VEPU_REG_ADDR_REC_LUMA 0x0fc +#define VEPU_REG_ADDR_REC_CHROMA 0x100 +#define VEPU_REG_CHECKPOINT(i) (0x104 + ((i) * 0x4)) +#define VEPU_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) +#define VEPU_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) +#define VEPU_REG_CHECKPOINT_RESULT(x) \ + ((((x) >> (16 - 16 * ((i) & 1))) & 0xffff) * 32) +#define VEPU_REG_VP8_SEG0_QUANT_AC_Y1 0x104 +#define VEPU_REG_VP8_SEG0_RND_AC_Y1(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y1(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_AC_Y1(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_DC_Y2 0x108 +#define VEPU_REG_VP8_SEG0_RND_DC_Y2(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y2(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_DC_Y2(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_AC_Y2 0x10c +#define VEPU_REG_VP8_SEG0_RND_AC_Y2(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y2(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_DC_CHR 0x110 +#define VEPU_REG_VP8_SEG0_RND_DC_CHR(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_DC_CHR(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_AC_CHR 0x114 +#define VEPU_REG_VP8_SEG0_RND_AC_CHR(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_AC_CHR(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_AC_CHR(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_DQUT 0x118 +#define VEPU_REG_VP8_MV_REF_IDX1(x) (((x) & 0x03) << 26) +#define VEPU_REG_VP8_SEG0_DQUT_DC_Y2(x) (((x) & 0x1ff) << 17) +#define VEPU_REG_VP8_SEG0_DQUT_AC_Y1(x) (((x) & 0x1ff) << 8) +#define VEPU_REG_VP8_SEG0_DQUT_DC_Y1(x) (((x) & 0xff) << 0) +#define VEPU_REG_CHKPT_WORD_ERR(i) (0x118 + ((i) * 0x4)) +#define VEPU_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) +#define VEPU_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) +#define VEPU_REG_VP8_SEG0_QUANT_DQUT_1 0x11c +#define VEPU_REG_VP8_SEGMENT_MAP_UPDATE BIT(30) +#define VEPU_REG_VP8_SEGMENT_EN BIT(29) +#define VEPU_REG_VP8_MV_REF_IDX2_EN BIT(28) +#define VEPU_REG_VP8_MV_REF_IDX2(x) (((x) & 0x03) << 26) +#define VEPU_REG_VP8_SEG0_DQUT_AC_CHR(x) (((x) & 0x1ff) << 17) +#define VEPU_REG_VP8_SEG0_DQUT_DC_CHR(x) (((x) & 0xff) << 9) +#define VEPU_REG_VP8_SEG0_DQUT_AC_Y2(x) (((x) & 0x1ff) << 0) +#define VEPU_REG_VP8_BOOL_ENC_VALUE 0x120 +#define VEPU_REG_CHKPT_DELTA_QP 0x124 +#define VEPU_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) +#define VEPU_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) +#define VEPU_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) +#define VEPU_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) +#define VEPU_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) +#define VEPU_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) +#define VEPU_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) +#define VEPU_REG_VP8_ENC_CTRL2 0x124 +#define VEPU_REG_VP8_ZERO_MV_PENALTY_FOR_REF2(x) (((x) & 0xff) << 24) +#define VEPU_REG_VP8_FILTER_SHARPNESS(x) (((x) & 0x07) << 21) +#define VEPU_REG_VP8_FILTER_LEVEL(x) (((x) & 0x3f) << 15) +#define VEPU_REG_VP8_DCT_PARTITION_CNT(x) (((x) & 0x03) << 13) +#define VEPU_REG_VP8_BOOL_ENC_VALUE_BITS(x) (((x) & 0x1f) << 8) +#define VEPU_REG_VP8_BOOL_ENC_RANGE(x) (((x) & 0xff) << 0) +#define VEPU_REG_ENC_CTRL1 0x128 +#define VEPU_REG_MAD_THRESHOLD(x) (((x) & 0x3f) << 24) +#define VEPU_REG_COMPLETED_SLICES(x) (((x) & 0xff) << 16) +#define VEPU_REG_IN_IMG_CTRL_FMT(x) (((x) & 0xf) << 4) +#define VEPU_REG_IN_IMG_ROTATE_MODE(x) (((x) & 0x3) << 2) +#define VEPU_REG_SIZE_TABLE_PRESENT BIT(0) +#define VEPU_REG_INTRA_INTER_MODE 0x12c +#define VEPU_REG_INTRA16X16_MODE(x) (((x) & 0xffff) << 16) +#define VEPU_REG_INTER_MODE(x) (((x) & 0xffff) << 0) +#define VEPU_REG_ENC_CTRL2 0x130 +#define VEPU_REG_PPS_INIT_QP(x) (((x) & 0x3f) << 26) +#define VEPU_REG_SLICE_FILTER_ALPHA(x) (((x) & 0xf) << 22) +#define VEPU_REG_SLICE_FILTER_BETA(x) (((x) & 0xf) << 18) +#define VEPU_REG_CHROMA_QP_OFFSET(x) (((x) & 0x1f) << 13) +#define VEPU_REG_FILTER_DISABLE BIT(5) +#define VEPU_REG_IDR_PIC_ID(x) (((x) & 0xf) << 1) +#define VEPU_REG_CONSTRAINED_INTRA_PREDICTION BIT(0) +#define VEPU_REG_ADDR_OUTPUT_STREAM 0x134 +#define VEPU_REG_ADDR_OUTPUT_CTRL 0x138 +#define VEPU_REG_ADDR_NEXT_PIC 0x13c +#define VEPU_REG_ADDR_MV_OUT 0x140 +#define VEPU_REG_ADDR_CABAC_TBL 0x144 +#define VEPU_REG_ROI1 0x148 +#define VEPU_REG_ROI1_TOP_MB(x) (((x) & 0xff) << 24) +#define VEPU_REG_ROI1_BOTTOM_MB(x) (((x) & 0xff) << 16) +#define VEPU_REG_ROI1_LEFT_MB(x) (((x) & 0xff) << 8) +#define VEPU_REG_ROI1_RIGHT_MB(x) (((x) & 0xff) << 0) +#define VEPU_REG_ROI2 0x14c +#define VEPU_REG_ROI2_TOP_MB(x) (((x) & 0xff) << 24) +#define VEPU_REG_ROI2_BOTTOM_MB(x) (((x) & 0xff) << 16) +#define VEPU_REG_ROI2_LEFT_MB(x) (((x) & 0xff) << 8) +#define VEPU_REG_ROI2_RIGHT_MB(x) (((x) & 0xff) << 0) +#define VEPU_REG_STABLE_MATRIX(i) (0x150 + ((i) * 0x4)) +#define VEPU_REG_STABLE_MOTION_SUM 0x174 +#define VEPU_REG_STABILIZATION_OUTPUT 0x178 +#define VEPU_REG_STABLE_MIN_VALUE(x) (((x) & 0xffffff) << 8) +#define VEPU_REG_STABLE_MODE_SEL(x) (((x) & 0x3) << 6) +#define VEPU_REG_STABLE_HOR_GMV(x) (((x) & 0x3f) << 0) +#define VEPU_REG_RGB2YUV_CONVERSION_COEF1 0x17c +#define VEPU_REG_RGB2YUV_CONVERSION_COEFB(x) (((x) & 0xffff) << 16) +#define VEPU_REG_RGB2YUV_CONVERSION_COEFA(x) (((x) & 0xffff) << 0) +#define VEPU_REG_RGB2YUV_CONVERSION_COEF2 0x180 +#define VEPU_REG_RGB2YUV_CONVERSION_COEFE(x) (((x) & 0xffff) << 16) +#define VEPU_REG_RGB2YUV_CONVERSION_COEFC(x) (((x) & 0xffff) << 0) +#define VEPU_REG_RGB2YUV_CONVERSION_COEF3 0x184 +#define VEPU_REG_RGB2YUV_CONVERSION_COEFF(x) (((x) & 0xffff) << 0) +#define VEPU_REG_RGB_MASK_MSB 0x188 +#define VEPU_REG_RGB_MASK_B_MSB(x) (((x) & 0x1f) << 16) +#define VEPU_REG_RGB_MASK_G_MSB(x) (((x) & 0x1f) << 8) +#define VEPU_REG_RGB_MASK_R_MSB(x) (((x) & 0x1f) << 0) +#define VEPU_REG_MV_PENALTY 0x18c +#define VEPU_REG_1MV_PENALTY(x) (((x) & 0x3ff) << 21) +#define VEPU_REG_QMV_PENALTY(x) (((x) & 0x3ff) << 11) +#define VEPU_REG_4MV_PENALTY(x) (((x) & 0x3ff) << 1) +#define VEPU_REG_SPLIT_MV_MODE_EN BIT(0) +#define VEPU_REG_QP_VAL 0x190 +#define VEPU_REG_H264_LUMA_INIT_QP(x) (((x) & 0x3f) << 26) +#define VEPU_REG_H264_QP_MAX(x) (((x) & 0x3f) << 20) +#define VEPU_REG_H264_QP_MIN(x) (((x) & 0x3f) << 14) +#define VEPU_REG_H264_CHKPT_DISTANCE(x) (((x) & 0xfff) << 0) +#define VEPU_REG_VP8_SEG0_QUANT_DC_Y1 0x190 +#define VEPU_REG_VP8_SEG0_RND_DC_Y1(x) (((x) & 0xff) << 23) +#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y1(x) (((x) & 0x1ff) << 14) +#define VEPU_REG_VP8_SEG0_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) +#define VEPU_REG_MVC_RELATE 0x198 +#define VEPU_REG_ZERO_MV_FAVOR_D2(x) (((x) & 0xf) << 20) +#define VEPU_REG_PENALTY_4X4MV(x) (((x) & 0x1ff) << 11) +#define VEPU_REG_MVC_VIEW_ID(x) (((x) & 0x7) << 8) +#define VEPU_REG_MVC_ANCHOR_PIC_FLAG BIT(7) +#define VEPU_REG_MVC_PRIORITY_ID(x) (((x) & 0x7) << 4) +#define VEPU_REG_MVC_TEMPORAL_ID(x) (((x) & 0x7) << 1) +#define VEPU_REG_MVC_INTER_VIEW_FLAG BIT(0) +#define VEPU_REG_ENCODE_START 0x19c +#define VEPU_REG_MB_HEIGHT(x) (((x) & 0x1ff) << 20) +#define VEPU_REG_MB_WIDTH(x) (((x) & 0x1ff) << 8) +#define VEPU_REG_FRAME_TYPE_INTER (0x0 << 6) +#define VEPU_REG_FRAME_TYPE_INTRA (0x1 << 6) +#define VEPU_REG_FRAME_TYPE_MVCINTER (0x2 << 6) +#define VEPU_REG_ENCODE_FORMAT_JPEG (0x2 << 4) +#define VEPU_REG_ENCODE_FORMAT_H264 (0x3 << 4) +#define VEPU_REG_ENCODE_ENABLE BIT(0) +#define VEPU_REG_MB_CTRL 0x1a0 +#define VEPU_REG_MB_CNT_OUT(x) (((x) & 0xffff) << 16) +#define VEPU_REG_MB_CNT_SET(x) (((x) & 0xffff) << 0) +#define VEPU_REG_DATA_ENDIAN 0x1a4 +#define VEPU_REG_INPUT_SWAP8 BIT(31) +#define VEPU_REG_INPUT_SWAP16 BIT(30) +#define VEPU_REG_INPUT_SWAP32 BIT(29) +#define VEPU_REG_OUTPUT_SWAP8 BIT(28) +#define VEPU_REG_OUTPUT_SWAP16 BIT(27) +#define VEPU_REG_OUTPUT_SWAP32 BIT(26) +#define VEPU_REG_TEST_IRQ BIT(24) +#define VEPU_REG_TEST_COUNTER(x) (((x) & 0xf) << 20) +#define VEPU_REG_TEST_REG BIT(19) +#define VEPU_REG_TEST_MEMORY BIT(18) +#define VEPU_REG_TEST_LEN(x) (((x) & 0x3ffff) << 0) +#define VEPU_REG_ENC_CTRL3 0x1a8 +#define VEPU_REG_PPS_ID(x) (((x) & 0xff) << 24) +#define VEPU_REG_INTRA_PRED_MODE(x) (((x) & 0xff) << 16) +#define VEPU_REG_FRAME_NUM(x) (((x) & 0xffff) << 0) +#define VEPU_REG_ENC_CTRL4 0x1ac +#define VEPU_REG_MV_PENALTY_16X8_8X16(x) (((x) & 0x3ff) << 20) +#define VEPU_REG_MV_PENALTY_8X8(x) (((x) & 0x3ff) << 10) +#define VEPU_REG_MV_PENALTY_8X4_4X8(x) (((x) & 0x3ff) << 0) +#define VEPU_REG_ADDR_VP8_PROB_CNT 0x1b0 +#define VEPU_REG_INTERRUPT 0x1b4 +#define VEPU_REG_INTERRUPT_NON BIT(28) +#define VEPU_REG_MV_WRITE_EN BIT(24) +#define VEPU_REG_RECON_WRITE_DIS BIT(20) +#define VEPU_REG_INTERRUPT_SLICE_READY_EN BIT(16) +#define VEPU_REG_CLK_GATING_EN BIT(12) +#define VEPU_REG_INTERRUPT_TIMEOUT_EN BIT(10) +#define VEPU_REG_INTERRUPT_RESET BIT(9) +#define VEPU_REG_INTERRUPT_DIS_BIT BIT(8) +#define VEPU_REG_INTERRUPT_TIMEOUT BIT(6) +#define VEPU_REG_INTERRUPT_BUFFER_FULL BIT(5) +#define VEPU_REG_INTERRUPT_BUS_ERROR BIT(4) +#define VEPU_REG_INTERRUPT_FUSE BIT(3) +#define VEPU_REG_INTERRUPT_SLICE_READY BIT(2) +#define VEPU_REG_INTERRUPT_FRAME_READY BIT(1) +#define VEPU_REG_INTERRUPT_BIT BIT(0) +#define VEPU_REG_DMV_PENALTY_TBL(i) (0x1E0 + ((i) * 0x4)) +#define VEPU_REG_DMV_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) +#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TBL(i) (0x260 + ((i) * 0x4)) +#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) + +/* vpu decoder register */ +#define VDPU_REG_DEC_CTRL0 0x0c8 // 50 +#define VDPU_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 25) +#define VDPU_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 13) +#define VDPU_REG_CONFIG_TILED_MODE_LSB BIT(12) +#define VDPU_REG_CONFIG_DEC_ADV_PRE_DIS BIT(11) +#define VDPU_REG_CONFIG_DEC_SCMD_DIS BIT(10) +#define VDPU_REG_DEC_CTRL0_SKIP_MODE BIT(9) +#define VDPU_REG_DEC_CTRL0_FILTERING_DIS BIT(8) +#define VDPU_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(7) +#define VDPU_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 1) +#define VDPU_REG_CONFIG_TILED_MODE_MSB(x) BIT(0) +#define VDPU_REG_CONFIG_DEC_OUT_TILED_E BIT(0) +#define VDPU_REG_STREAM_LEN 0x0cc +#define VDPU_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) +#define VDPU_REG_DEC_STREAM_LEN_HI BIT(24) +#define VDPU_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) +#define VDPU_REG_ERROR_CONCEALMENT 0x0d0 +#define VDPU_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 17) +#define VDPU_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 8) +#define VDPU_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 0) +#define VDPU_REG_DEC_FORMAT 0x0d4 +#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) +#define VDPU_REG_DATA_ENDIAN 0x0d8 +#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) +#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) +#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) +#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) +#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) +#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) +#define VDPU_REG_INTERRUPT 0x0dc +#define VDPU_REG_INTERRUPT_DEC_TIMEOUT BIT(13) +#define VDPU_REG_INTERRUPT_DEC_ERROR_INT BIT(12) +#define VDPU_REG_INTERRUPT_DEC_PIC_INF BIT(10) +#define VDPU_REG_INTERRUPT_DEC_SLICE_INT BIT(9) +#define VDPU_REG_INTERRUPT_DEC_ASO_INT BIT(8) +#define VDPU_REG_INTERRUPT_DEC_BUFFER_INT BIT(6) +#define VDPU_REG_INTERRUPT_DEC_BUS_INT BIT(5) +#define VDPU_REG_INTERRUPT_DEC_RDY_INT BIT(4) +#define VDPU_REG_INTERRUPT_DEC_IRQ_DIS BIT(1) +#define VDPU_REG_INTERRUPT_DEC_IRQ BIT(0) +#define VDPU_REG_AXI_CTRL 0x0e0 +#define VDPU_REG_AXI_DEC_SEL BIT(23) +#define VDPU_REG_CONFIG_DEC_DATA_DISC_E BIT(22) +#define VDPU_REG_PARAL_BUS_E(x) BIT(21) +#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) +#define VDPU_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 8) +#define VDPU_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 0) +#define VDPU_REG_EN_FLAGS 0x0e4 +#define VDPU_REG_AHB_HLOCK_E BIT(31) +#define VDPU_REG_CACHE_E BIT(29) +#define VDPU_REG_PREFETCH_SINGLE_CHANNEL_E BIT(28) +#define VDPU_REG_INTRA_3_CYCLE_ENHANCE BIT(27) +#define VDPU_REG_INTRA_DOUBLE_SPEED BIT(26) +#define VDPU_REG_INTER_DOUBLE_SPEED BIT(25) +#define VDPU_REG_DEC_CTRL3_START_CODE_E BIT(22) +#define VDPU_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(21) +#define VDPU_REG_DEC_CTRL0_RLC_MODE_E BIT(20) +#define VDPU_REG_DEC_CTRL0_DIVX3_E BIT(19) +#define VDPU_REG_DEC_CTRL0_PJPEG_E BIT(18) +#define VDPU_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(17) +#define VDPU_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(16) +#define VDPU_REG_DEC_CTRL0_PIC_B_E BIT(15) +#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) +#define VDPU_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(13) +#define VDPU_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(12) +#define VDPU_REG_DEC_CTRL0_SORENSON_E BIT(11) +#define VDPU_REG_DEC_CTRL0_WRITE_MVS_E BIT(10) +#define VDPU_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(9) +#define VDPU_REG_DEC_CTRL0_REFTOPFIRST_E BIT(8) +#define VDPU_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(7) +#define VDPU_REG_DEC_CTRL0_PICORD_COUNT_E BIT(6) +#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) +#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) +#define VDPU_REG_DEC_CTRL0_DEC_OUT_DIS BIT(2) +#define VDPU_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(1) +#define VDPU_REG_INTERRUPT_DEC_E BIT(0) +#define VDPU_REG_SOFT_RESET 0x0e8 +#define VDPU_REG_PRED_FLT 0x0ec +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_ADDITIONAL_CHROMA_ADDRESS 0x0f0 +#define VDPU_REG_ADDR_QTABLE 0x0f4 +#define VDPU_REG_DIRECT_MV_ADDR 0x0f8 +#define VDPU_REG_ADDR_DST 0x0fc +#define VDPU_REG_ADDR_STR 0x100 +#define VDPU_REG_REFBUF_RELATED 0x104 +#define VDPU_REG_FWD_PIC(i) (0x128 + ((i) * 0x4)) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) +#define VDPU_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) +#define VDPU_REG_REF_PIC(i) (0x130 + ((i) * 0x4)) +#define VDPU_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) +#define VDPU_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) +#define VDPU_REG_H264_ADDR_REF(i) (0x150 + ((i) * 0x4)) +#define VDPU_REG_ADDR_REF_FIELD_E BIT(1) +#define VDPU_REG_ADDR_REF_TOPC_E BIT(0) +#define VDPU_REG_INITIAL_REF_PIC_LIST0 0x190 +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F5(x) (((x) & 0x1f) << 25) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F4(x) (((x) & 0x1f) << 20) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F3(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST1 0x194 +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F11(x) (((x) & 0x1f) << 25) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F10(x) (((x) & 0x1f) << 20) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F9(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F8(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F7(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F6(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST2 0x198 +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F14(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F13(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F12(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST3 0x19c +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B5(x) (((x) & 0x1f) << 25) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B4(x) (((x) & 0x1f) << 20) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B3(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST4 0x1a0 +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B11(x) (((x) & 0x1f) << 25) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B10(x) (((x) & 0x1f) << 20) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B9(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B8(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B7(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B6(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST5 0x1a4 +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B14(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B13(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B12(x) (((x) & 0x1f) << 0) +#define VDPU_REG_INITIAL_REF_PIC_LIST6 0x1a8 +#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) +#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) +#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) +#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) +#define VDPU_REG_LT_REF 0x1ac +#define VDPU_REG_VALID_REF 0x1b0 +#define VDPU_REG_H264_PIC_MB_SIZE 0x1b8 +#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 22) +#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 17) +#define VDPU_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 9) +#define VDPU_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 0) +#define VDPU_REG_H264_CTRL 0x1bc +#define VDPU_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 16) +#define VDPU_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) +#define VDPU_REG_CURRENT_FRAME 0x1c0 +#define VDPU_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(31) +#define VDPU_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(30) +#define VDPU_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) +#define VDPU_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) +#define VDPU_REG_REF_FRAME 0x1c4 +#define VDPU_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 16) +#define VDPU_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) +#define VDPU_REG_DEC_CTRL6 0x1c8 +#define VDPU_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) +#define VDPU_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) +#define VDPU_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) +#define VDPU_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) +#define VDPU_REG_ENABLE_FLAG 0x1cc +#define VDPU_REG_DEC_CTRL5_IDR_PIC_E BIT(8) +#define VDPU_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(7) +#define VDPU_REG_DEC_CTRL4_BLACKWHITE_E BIT(6) +#define VDPU_REG_DEC_CTRL4_CABAC_E BIT(5) +#define VDPU_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(4) +#define VDPU_REG_DEC_CTRL5_CONST_INTRA_E BIT(3) +#define VDPU_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(2) +#define VDPU_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(1) +#define VDPU_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) +#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 +#define VDPU_REG_DEC_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) +#define VDPU_REG_DEC_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) +#define VDPU_REG_DEC_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) +#define VDPU_REG_DEC_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) +#define VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) +#define VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) +#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 +#define VDPU_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) +#define VDPU_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) +#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) +#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) +#define VDPU_REG_VP8_CTRL0 0x1e8 +#define VDPU_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) +#define VDPU_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) +#define VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) +#define VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) +#define VDPU_REG_VP8_DATA_VAL 0x1f0 +#define VDPU_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) +#define VDPU_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) +#define VDPU_REG_PRED_FLT7 0x1f4 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT8 0x1f8 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT9 0x1fc +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT10 0x200 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) +#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) +#define VDPU_REG_FILTER_LEVEL 0x204 +#define VDPU_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) +#define VDPU_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) +#define VDPU_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) +#define VDPU_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) +#define VDPU_REG_VP8_QUANTER0 0x208 +#define VDPU_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) +#define VDPU_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) +#define VDPU_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) +#define VDPU_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) +#define VDPU_REG_VP8_ADDR_REF0 0x20c +#define VDPU_REG_FILTER_MB_ADJ 0x210 +#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) +#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) +#define VDPU_REG_FILT_MB_ADJ_0(x) (((x) & 0x7f) << 21) +#define VDPU_REG_FILT_MB_ADJ_1(x) (((x) & 0x7f) << 14) +#define VDPU_REG_FILT_MB_ADJ_2(x) (((x) & 0x7f) << 7) +#define VDPU_REG_FILT_MB_ADJ_3(x) (((x) & 0x7f) << 0) +#define VDPU_REG_FILTER_REF_ADJ 0x214 +#define VDPU_REG_REF_PIC_ADJ_0(x) (((x) & 0x7f) << 21) +#define VDPU_REG_REF_PIC_ADJ_1(x) (((x) & 0x7f) << 14) +#define VDPU_REG_REF_PIC_ADJ_2(x) (((x) & 0x7f) << 7) +#define VDPU_REG_REF_PIC_ADJ_3(x) (((x) & 0x7f) << 0) +#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) +#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) +#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) +#define VDPU_REG_VP8_DCT_BASE(i) (0x230 + ((i) * 0x4)) +#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 +#define VDPU_REG_VP8_ADDR_REF1 0x250 +#define VDPU_REG_VP8_SEGMENT_VAL 0x254 +#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) +#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) +#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) +#define VDPU_REG_VP8_DCT_START_BIT2 0x258 +#define VDPU_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) +#define VDPU_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) +#define VDPU_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) +#define VDPU_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) +#define VDPU_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) +#define VDPU_REG_VP8_QUANTER1 0x25c +#define VDPU_REG_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) +#define VDPU_REG_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) +#define VDPU_REG_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) +#define VDPU_REG_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) +#define VDPU_REG_VP8_QUANTER2 0x260 +#define VDPU_REG_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) +#define VDPU_REG_REF_PIC_QUANT_4(x) (((x) & 0x7ff) << 11) +#define VDPU_REG_REF_PIC_QUANT_5(x) (((x) & 0x7ff) << 0) +#define VDPU_REG_PRED_FLT1 0x264 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT2 0x268 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT3 0x26c +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT4 0x270 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT5 0x274 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1(x) (((x) & 0x3ff) << 2) +#define VDPU_REG_PRED_FLT6 0x278 +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2(x) (((x) & 0x3ff) << 22) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3(x) (((x) & 0x3ff) << 12) +#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0(x) (((x) & 0x3ff) << 2) + +#endif /* ROCKCHIP_VPU2_REGS_H_ */ diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c new file mode 100644 index 000000000000..8de6fd2e8eef --- /dev/null +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Jeffy Chen + */ + +#include + +#include "hantro.h" +#include "hantro_jpeg.h" +#include "hantro_g1_regs.h" +#include "hantro_h1_regs.h" +#include "rockchip_vpu2_regs.h" + +#define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000) +#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000) + +/* + * Supported formats. + */ + +static const struct hantro_fmt rockchip_vpu_enc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .codec_mode = HANTRO_MODE_NONE, + .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420P, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .codec_mode = HANTRO_MODE_NONE, + .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420SP, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUYV422, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .codec_mode = HANTRO_MODE_NONE, + .enc_fmt = ROCKCHIP_VPU_ENC_FMT_UYVY422, + }, + { + .fourcc = V4L2_PIX_FMT_JPEG, + .codec_mode = HANTRO_MODE_JPEG_ENC, + .max_depth = 2, + .header_size = JPEG_HEADER_SIZE, + .frmsize = { + .min_width = 96, + .max_width = 8192, + .step_width = MB_DIM, + .min_height = 32, + .max_height = 8192, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt rockchip_vpu1_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt rk3066_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt rk3288_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_4K_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_4K_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_4K_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_4K_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt rockchip_vdpu2_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt rk3399_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_FHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_FHD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static irqreturn_t rockchip_vpu1_vepu_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vepu_read(vpu, H1_REG_INTERRUPT); + state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vepu_write(vpu, 0, H1_REG_INTERRUPT); + vepu_write(vpu, 0, H1_REG_AXI_CTRL); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_vpu2_vdpu_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, VDPU_REG_INTERRUPT); + state = (status & VDPU_REG_INTERRUPT_DEC_IRQ) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); + vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_vpu2_vepu_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vepu_read(vpu, VEPU_REG_INTERRUPT); + state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vepu_write(vpu, 0, VEPU_REG_INTERRUPT); + vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); + + hantro_irq_done(vpu, state); + + return IRQ_HANDLED; +} + +static int rk3036_vpu_hw_init(struct hantro_dev *vpu) +{ + /* Bump ACLK to max. possible freq. to improve performance. */ + clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); + return 0; +} + +static int rk3066_vpu_hw_init(struct hantro_dev *vpu) +{ + /* Bump ACLKs to max. possible freq. to improve performance. */ + clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); + clk_set_rate(vpu->clocks[2].clk, RK3066_ACLK_MAX_FREQ); + return 0; +} + +static int rockchip_vpu_hw_init(struct hantro_dev *vpu) +{ + /* Bump ACLK to max. possible freq. to improve performance. */ + clk_set_rate(vpu->clocks[0].clk, RK3288_ACLK_MAX_FREQ); + return 0; +} + +static void rk3066_vpu_dec_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); +} + +static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + vepu_write(vpu, H1_REG_INTERRUPT_DIS_BIT, H1_REG_INTERRUPT); + vepu_write(vpu, 0, H1_REG_ENC_CTRL); + vepu_write(vpu, 0, H1_REG_AXI_CTRL); +} + +static void rockchip_vpu2_dec_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); + vdpu_write(vpu, 0, VDPU_REG_EN_FLAGS); + vdpu_write(vpu, 1, VDPU_REG_SOFT_RESET); +} + +static void rockchip_vpu2_enc_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT); + vepu_write(vpu, 0, VEPU_REG_ENCODE_START); + vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); +} + +/* + * Supported codec ops. + */ +static const struct hantro_codec_ops rk3036_vpu_codec_ops[] = { + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = hantro_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = hantro_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = hantro_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, +}; + +static const struct hantro_codec_ops rk3066_vpu_codec_ops[] = { + [HANTRO_MODE_JPEG_ENC] = { + .run = hantro_h1_jpeg_enc_run, + .reset = rockchip_vpu1_enc_reset, + .done = hantro_h1_jpeg_enc_done, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = rk3066_vpu_dec_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = rk3066_vpu_dec_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = rk3066_vpu_dec_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, +}; + +static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { + [HANTRO_MODE_JPEG_ENC] = { + .run = hantro_h1_jpeg_enc_run, + .reset = rockchip_vpu1_enc_reset, + .done = hantro_h1_jpeg_enc_done, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = hantro_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = hantro_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = hantro_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, +}; + +static const struct hantro_codec_ops rk3399_vpu_codec_ops[] = { + [HANTRO_MODE_JPEG_ENC] = { + .run = rockchip_vpu2_jpeg_enc_run, + .reset = rockchip_vpu2_enc_reset, + .done = rockchip_vpu2_jpeg_enc_done, + }, + [HANTRO_MODE_H264_DEC] = { + .run = rockchip_vpu2_h264_dec_run, + .reset = rockchip_vpu2_dec_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, + [HANTRO_MODE_MPEG2_DEC] = { + .run = rockchip_vpu2_mpeg2_dec_run, + .reset = rockchip_vpu2_dec_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = rockchip_vpu2_vp8_dec_run, + .reset = rockchip_vpu2_dec_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, +}; + +static const struct hantro_codec_ops rk3568_vepu_codec_ops[] = { + [HANTRO_MODE_JPEG_ENC] = { + .run = rockchip_vpu2_jpeg_enc_run, + .reset = rockchip_vpu2_enc_reset, + .done = rockchip_vpu2_jpeg_enc_done, + }, +}; + +/* + * VPU variant. + */ + +static const struct hantro_irq rockchip_vdpu1_irqs[] = { + { "vdpu", hantro_g1_irq }, +}; + +static const struct hantro_irq rockchip_vpu1_irqs[] = { + { "vepu", rockchip_vpu1_vepu_irq }, + { "vdpu", hantro_g1_irq }, +}; + +static const struct hantro_irq rockchip_vdpu2_irqs[] = { + { "vdpu", rockchip_vpu2_vdpu_irq }, +}; + +static const struct hantro_irq rockchip_vpu2_irqs[] = { + { "vepu", rockchip_vpu2_vepu_irq }, + { "vdpu", rockchip_vpu2_vdpu_irq }, +}; + +static const struct hantro_irq rk3568_vepu_irqs[] = { + { "vepu", rockchip_vpu2_vepu_irq }, +}; + +static const char * const rk3066_vpu_clk_names[] = { + "aclk_vdpu", "hclk_vdpu", + "aclk_vepu", "hclk_vepu" +}; + +static const char * const rockchip_vpu_clk_names[] = { + "aclk", "hclk" +}; + +/* VDPU1/VEPU1 */ + +const struct hantro_variant rk3036_vpu_variant = { + .dec_offset = 0x400, + .dec_fmts = rk3066_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), + .postproc_fmts = rockchip_vpu1_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = rk3036_vpu_codec_ops, + .irqs = rockchip_vdpu1_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vdpu1_irqs), + .init = rk3036_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; + +/* + * Despite this variant has separate clocks for decoder and encoder, + * it's still required to enable all four of them for either decoding + * or encoding and we can't split it in separate g1/h1 variants. + */ +const struct hantro_variant rk3066_vpu_variant = { + .enc_offset = 0x0, + .enc_fmts = rockchip_vpu_enc_fmts, + .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), + .dec_offset = 0x400, + .dec_fmts = rk3066_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), + .postproc_fmts = rockchip_vpu1_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | + HANTRO_VP8_DECODER | HANTRO_H264_DECODER, + .codec_ops = rk3066_vpu_codec_ops, + .irqs = rockchip_vpu1_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), + .init = rk3066_vpu_hw_init, + .clk_names = rk3066_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rk3066_vpu_clk_names) +}; + +const struct hantro_variant rk3288_vpu_variant = { + .enc_offset = 0x0, + .enc_fmts = rockchip_vpu_enc_fmts, + .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), + .dec_offset = 0x400, + .dec_fmts = rk3288_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3288_vpu_dec_fmts), + .postproc_fmts = rockchip_vpu1_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | + HANTRO_VP8_DECODER | HANTRO_H264_DECODER, + .codec_ops = rk3288_vpu_codec_ops, + .irqs = rockchip_vpu1_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), + .init = rockchip_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; + +/* VDPU2/VEPU2 */ + +const struct hantro_variant rk3328_vpu_variant = { + .dec_offset = 0x400, + .dec_fmts = rockchip_vdpu2_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = rk3399_vpu_codec_ops, + .irqs = rockchip_vdpu2_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), + .init = rockchip_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names), +}; + +/* + * H.264 decoding explicitly disabled in RK3399. + * This ensures userspace applications use the Rockchip VDEC core, + * which has better performance. + */ +const struct hantro_variant rk3399_vpu_variant = { + .enc_offset = 0x0, + .enc_fmts = rockchip_vpu_enc_fmts, + .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), + .dec_offset = 0x400, + .dec_fmts = rk3399_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts), + .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | + HANTRO_VP8_DECODER, + .codec_ops = rk3399_vpu_codec_ops, + .irqs = rockchip_vpu2_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), + .init = rockchip_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; + +const struct hantro_variant rk3568_vepu_variant = { + .enc_offset = 0x0, + .enc_fmts = rockchip_vpu_enc_fmts, + .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), + .codec = HANTRO_JPEG_ENCODER, + .codec_ops = rk3568_vepu_codec_ops, + .irqs = rk3568_vepu_irqs, + .num_irqs = ARRAY_SIZE(rk3568_vepu_irqs), + .init = rockchip_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; + +const struct hantro_variant rk3568_vpu_variant = { + .dec_offset = 0x400, + .dec_fmts = rockchip_vdpu2_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), + .codec = HANTRO_MPEG2_DECODER | + HANTRO_VP8_DECODER | HANTRO_H264_DECODER, + .codec_ops = rk3399_vpu_codec_ops, + .irqs = rockchip_vdpu2_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), + .init = rockchip_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; + +const struct hantro_variant px30_vpu_variant = { + .enc_offset = 0x0, + .enc_fmts = rockchip_vpu_enc_fmts, + .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), + .dec_offset = 0x400, + .dec_fmts = rockchip_vdpu2_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), + .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | + HANTRO_VP8_DECODER | HANTRO_H264_DECODER, + .codec_ops = rk3399_vpu_codec_ops, + .irqs = rockchip_vpu2_irqs, + .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), + .init = rk3036_vpu_hw_init, + .clk_names = rockchip_vpu_clk_names, + .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) +}; diff --git a/drivers/media/platform/verisilicon/sama5d4_vdec_hw.c b/drivers/media/platform/verisilicon/sama5d4_vdec_hw.c new file mode 100644 index 000000000000..b205e2db5b04 --- /dev/null +++ b/drivers/media/platform/verisilicon/sama5d4_vdec_hw.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VDEC driver + * + * Copyright (C) 2021 Collabora Ltd, Emil Velikov + */ + +#include "hantro.h" + +/* + * Supported formats. + */ + +static const struct hantro_fmt sama5d4_vdec_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_HD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_HD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +static const struct hantro_fmt sama5d4_vdec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_HD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_HD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_HD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_HD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_HD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_HD_HEIGHT, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_HD_WIDTH, + .step_width = MB_DIM, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_HD_HEIGHT, + .step_height = MB_DIM, + }, + }, +}; + +/* + * Supported codec ops. + */ + +static const struct hantro_codec_ops sama5d4_vdec_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = hantro_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = hantro_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = hantro_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + +static const struct hantro_irq sama5d4_irqs[] = { + { "vdec", hantro_g1_irq }, +}; + +static const char * const sama5d4_clk_names[] = { "vdec_clk" }; + +const struct hantro_variant sama5d4_vdec_variant = { + .dec_fmts = sama5d4_vdec_fmts, + .num_dec_fmts = ARRAY_SIZE(sama5d4_vdec_fmts), + .postproc_fmts = sama5d4_vdec_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(sama5d4_vdec_postproc_fmts), + .postproc_ops = &hantro_g1_postproc_ops, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = sama5d4_vdec_codec_ops, + .irqs = sama5d4_irqs, + .num_irqs = ARRAY_SIZE(sama5d4_irqs), + .clk_names = sama5d4_clk_names, + .num_clocks = ARRAY_SIZE(sama5d4_clk_names), +}; diff --git a/drivers/media/platform/verisilicon/sunxi_vpu_hw.c b/drivers/media/platform/verisilicon/sunxi_vpu_hw.c new file mode 100644 index 000000000000..02ce8b064a8f --- /dev/null +++ b/drivers/media/platform/verisilicon/sunxi_vpu_hw.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner Hantro G2 VPU codec driver + * + * Copyright (C) 2021 Jernej Skrabec + */ + +#include + +#include "hantro.h" + +static const struct hantro_fmt sunxi_vpu_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = 32, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = 32, + }, + }, + { + .fourcc = V4L2_PIX_FMT_P010, + .codec_mode = HANTRO_MODE_NONE, + .postprocessed = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = 32, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = 32, + }, + }, +}; + +static const struct hantro_fmt sunxi_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12_4L4, + .codec_mode = HANTRO_MODE_NONE, + .match_depth = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = 32, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = 32, + }, + }, + { + .fourcc = V4L2_PIX_FMT_P010_4L4, + .codec_mode = HANTRO_MODE_NONE, + .match_depth = true, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = 32, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = 32, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP9_FRAME, + .codec_mode = HANTRO_MODE_VP9_DEC, + .max_depth = 2, + .frmsize = { + .min_width = FMT_MIN_WIDTH, + .max_width = FMT_UHD_WIDTH, + .step_width = 32, + .min_height = FMT_MIN_HEIGHT, + .max_height = FMT_UHD_HEIGHT, + .step_height = 32, + }, + }, +}; + +static int sunxi_vpu_hw_init(struct hantro_dev *vpu) +{ + clk_set_rate(vpu->clocks[0].clk, 300000000); + + return 0; +} + +static void sunxi_vpu_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + reset_control_reset(vpu->resets); +} + +static const struct hantro_codec_ops sunxi_vpu_codec_ops[] = { + [HANTRO_MODE_VP9_DEC] = { + .run = hantro_g2_vp9_dec_run, + .done = hantro_g2_vp9_dec_done, + .reset = sunxi_vpu_reset, + .init = hantro_vp9_dec_init, + .exit = hantro_vp9_dec_exit, + }, +}; + +static const struct hantro_irq sunxi_irqs[] = { + { NULL, hantro_g2_irq }, +}; + +static const char * const sunxi_clk_names[] = { "mod", "bus" }; + +const struct hantro_variant sunxi_vpu_variant = { + .dec_fmts = sunxi_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(sunxi_vpu_dec_fmts), + .postproc_fmts = sunxi_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(sunxi_vpu_postproc_fmts), + .postproc_ops = &hantro_g2_postproc_ops, + .codec = HANTRO_VP9_DECODER, + .codec_ops = sunxi_vpu_codec_ops, + .init = sunxi_vpu_hw_init, + .irqs = sunxi_irqs, + .num_irqs = ARRAY_SIZE(sunxi_irqs), + .clk_names = sunxi_clk_names, + .num_clocks = ARRAY_SIZE(sunxi_clk_names), + .double_buffer = 1, + .legacy_regs = 1, + .late_postproc = 1, +}; diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 56eeecb03b31..d4f03b203ae5 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -22,8 +22,6 @@ if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order source "drivers/staging/media/atomisp/Kconfig" -source "drivers/staging/media/hantro/Kconfig" - source "drivers/staging/media/imx/Kconfig" source "drivers/staging/media/ipu3/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 874df4953729..a387692b84f2 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ obj-$(CONFIG_VIDEO_STKWEBCAM) += deprecated/stkwebcam/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ -obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ diff --git a/drivers/staging/media/hantro/Kconfig b/drivers/staging/media/hantro/Kconfig deleted file mode 100644 index 0172a6822ec2..000000000000 --- a/drivers/staging/media/hantro/Kconfig +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VIDEO_HANTRO - tristate "Hantro VPU driver" - depends on ARCH_MXC || ARCH_ROCKCHIP || ARCH_AT91 || ARCH_SUNXI || COMPILE_TEST - depends on VIDEO_DEV - select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_MEM2MEM_DEV - select V4L2_H264 - select V4L2_VP9 - help - Support for the Hantro IP based Video Processing Units present on - Rockchip and NXP i.MX8M SoCs, which accelerate video and image - encoding and decoding. - To compile this driver as a module, choose M here: the module - will be called hantro-vpu. - -config VIDEO_HANTRO_IMX8M - bool "Hantro VPU i.MX8M support" - depends on VIDEO_HANTRO - depends on ARCH_MXC || COMPILE_TEST - default y - help - Enable support for i.MX8M SoCs. - -config VIDEO_HANTRO_SAMA5D4 - bool "Hantro VDEC SAMA5D4 support" - depends on VIDEO_HANTRO - depends on ARCH_AT91 || COMPILE_TEST - default y - help - Enable support for Microchip SAMA5D4 SoCs. - -config VIDEO_HANTRO_ROCKCHIP - bool "Hantro VPU Rockchip support" - depends on VIDEO_HANTRO - depends on ARCH_ROCKCHIP || COMPILE_TEST - default y - help - Enable support for RK3288, RK3328, and RK3399 SoCs. - -config VIDEO_HANTRO_SUNXI - bool "Hantro VPU Allwinner support" - depends on VIDEO_HANTRO - depends on ARCH_SUNXI || COMPILE_TEST - default y - help - Enable support for H6 SoC. diff --git a/drivers/staging/media/hantro/Makefile b/drivers/staging/media/hantro/Makefile deleted file mode 100644 index ebd5ede7bef7..000000000000 --- a/drivers/staging/media/hantro/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_VIDEO_HANTRO) += hantro-vpu.o - -hantro-vpu-y += \ - hantro_drv.o \ - hantro_v4l2.o \ - hantro_postproc.o \ - hantro_h1_jpeg_enc.o \ - hantro_g1.o \ - hantro_g1_h264_dec.o \ - hantro_g1_mpeg2_dec.o \ - hantro_g1_vp8_dec.o \ - hantro_g2.o \ - hantro_g2_hevc_dec.o \ - hantro_g2_vp9_dec.o \ - rockchip_vpu2_hw_jpeg_enc.o \ - rockchip_vpu2_hw_h264_dec.o \ - rockchip_vpu2_hw_mpeg2_dec.o \ - rockchip_vpu2_hw_vp8_dec.o \ - hantro_jpeg.o \ - hantro_h264.o \ - hantro_hevc.o \ - hantro_mpeg2.o \ - hantro_vp8.o \ - hantro_vp9.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \ - imx8m_vpu_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \ - sama5d4_vdec_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ - rockchip_vpu_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ - sunxi_vpu_hw.o diff --git a/drivers/staging/media/hantro/TODO b/drivers/staging/media/hantro/TODO deleted file mode 100644 index 8483ff482146..000000000000 --- a/drivers/staging/media/hantro/TODO +++ /dev/null @@ -1,2 +0,0 @@ -The V4L controls for the HEVC CODEC are not yet part of the stable uABI, -we are keeping this driver in staging until the HEVC uABI has been merged. diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h deleted file mode 100644 index 2989ebc631cc..000000000000 --- a/drivers/staging/media/hantro/hantro.h +++ /dev/null @@ -1,485 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#ifndef HANTRO_H_ -#define HANTRO_H_ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "hantro_hw.h" - -struct hantro_ctx; -struct hantro_codec_ops; -struct hantro_postproc_ops; - -#define HANTRO_JPEG_ENCODER BIT(0) -#define HANTRO_ENCODERS 0x0000ffff -#define HANTRO_MPEG2_DECODER BIT(16) -#define HANTRO_VP8_DECODER BIT(17) -#define HANTRO_H264_DECODER BIT(18) -#define HANTRO_HEVC_DECODER BIT(19) -#define HANTRO_VP9_DECODER BIT(20) -#define HANTRO_DECODERS 0xffff0000 - -/** - * struct hantro_irq - irq handler and name - * - * @name: irq name for device tree lookup - * @handler: interrupt handler - */ -struct hantro_irq { - const char *name; - irqreturn_t (*handler)(int irq, void *priv); -}; - -/** - * struct hantro_variant - information about VPU hardware variant - * - * @enc_offset: Offset from VPU base to encoder registers. - * @dec_offset: Offset from VPU base to decoder registers. - * @enc_fmts: Encoder formats. - * @num_enc_fmts: Number of encoder formats. - * @dec_fmts: Decoder formats. - * @num_dec_fmts: Number of decoder formats. - * @postproc_fmts: Post-processor formats. - * @num_postproc_fmts: Number of post-processor formats. - * @postproc_ops: Post-processor ops. - * @codec: Supported codecs - * @codec_ops: Codec ops. - * @init: Initialize hardware, optional. - * @runtime_resume: reenable hardware after power gating, optional. - * @irqs: array of irq names and interrupt handlers - * @num_irqs: number of irqs in the array - * @clk_names: array of clock names - * @num_clocks: number of clocks in the array - * @reg_names: array of register range names - * @num_regs: number of register range names in the array - * @double_buffer: core needs double buffering - * @legacy_regs: core uses legacy register set - * @late_postproc: postproc must be set up at the end of the job - */ -struct hantro_variant { - unsigned int enc_offset; - unsigned int dec_offset; - const struct hantro_fmt *enc_fmts; - unsigned int num_enc_fmts; - const struct hantro_fmt *dec_fmts; - unsigned int num_dec_fmts; - const struct hantro_fmt *postproc_fmts; - unsigned int num_postproc_fmts; - const struct hantro_postproc_ops *postproc_ops; - unsigned int codec; - const struct hantro_codec_ops *codec_ops; - int (*init)(struct hantro_dev *vpu); - int (*runtime_resume)(struct hantro_dev *vpu); - const struct hantro_irq *irqs; - int num_irqs; - const char * const *clk_names; - int num_clocks; - const char * const *reg_names; - int num_regs; - unsigned int double_buffer : 1; - unsigned int legacy_regs : 1; - unsigned int late_postproc : 1; -}; - -/** - * enum hantro_codec_mode - codec operating mode. - * @HANTRO_MODE_NONE: No operating mode. Used for RAW video formats. - * @HANTRO_MODE_JPEG_ENC: JPEG encoder. - * @HANTRO_MODE_H264_DEC: H264 decoder. - * @HANTRO_MODE_MPEG2_DEC: MPEG-2 decoder. - * @HANTRO_MODE_VP8_DEC: VP8 decoder. - * @HANTRO_MODE_HEVC_DEC: HEVC decoder. - * @HANTRO_MODE_VP9_DEC: VP9 decoder. - */ -enum hantro_codec_mode { - HANTRO_MODE_NONE = -1, - HANTRO_MODE_JPEG_ENC, - HANTRO_MODE_H264_DEC, - HANTRO_MODE_MPEG2_DEC, - HANTRO_MODE_VP8_DEC, - HANTRO_MODE_HEVC_DEC, - HANTRO_MODE_VP9_DEC, -}; - -/* - * struct hantro_ctrl - helper type to declare supported controls - * @codec: codec id this control belong to (HANTRO_JPEG_ENCODER, etc.) - * @cfg: control configuration - */ -struct hantro_ctrl { - unsigned int codec; - struct v4l2_ctrl_config cfg; -}; - -/* - * struct hantro_func - Hantro VPU functionality - * - * @id: processing functionality ID (can be - * %MEDIA_ENT_F_PROC_VIDEO_ENCODER or - * %MEDIA_ENT_F_PROC_VIDEO_DECODER) - * @vdev: &struct video_device that exposes the encoder or - * decoder functionality - * @source_pad: &struct media_pad with the source pad. - * @sink: &struct media_entity pointer with the sink entity - * @sink_pad: &struct media_pad with the sink pad. - * @proc: &struct media_entity pointer with the M2M device itself. - * @proc_pads: &struct media_pad with the @proc pads. - * @intf_devnode: &struct media_intf devnode pointer with the interface - * with controls the M2M device. - * - * Contains everything needed to attach the video device to the media device. - */ -struct hantro_func { - unsigned int id; - struct video_device vdev; - struct media_pad source_pad; - struct media_entity sink; - struct media_pad sink_pad; - struct media_entity proc; - struct media_pad proc_pads[2]; - struct media_intf_devnode *intf_devnode; -}; - -static inline struct hantro_func * -hantro_vdev_to_func(struct video_device *vdev) -{ - return container_of(vdev, struct hantro_func, vdev); -} - -/** - * struct hantro_dev - driver data - * @v4l2_dev: V4L2 device to register video devices for. - * @m2m_dev: mem2mem device associated to this device. - * @mdev: media device associated to this device. - * @encoder: encoder functionality. - * @decoder: decoder functionality. - * @pdev: Pointer to VPU platform device. - * @dev: Pointer to device for convenient logging using - * dev_ macros. - * @clocks: Array of clock handles. - * @resets: Array of reset handles. - * @reg_bases: Mapped addresses of VPU registers. - * @enc_base: Mapped address of VPU encoder register for convenience. - * @dec_base: Mapped address of VPU decoder register for convenience. - * @ctrl_base: Mapped address of VPU control block. - * @vpu_mutex: Mutex to synchronize V4L2 calls. - * @irqlock: Spinlock to synchronize access to data structures - * shared with interrupt handlers. - * @variant: Hardware variant-specific parameters. - * @watchdog_work: Delayed work for hardware timeout handling. - */ -struct hantro_dev { - struct v4l2_device v4l2_dev; - struct v4l2_m2m_dev *m2m_dev; - struct media_device mdev; - struct hantro_func *encoder; - struct hantro_func *decoder; - struct platform_device *pdev; - struct device *dev; - struct clk_bulk_data *clocks; - struct reset_control *resets; - void __iomem **reg_bases; - void __iomem *enc_base; - void __iomem *dec_base; - void __iomem *ctrl_base; - - struct mutex vpu_mutex; /* video_device lock */ - spinlock_t irqlock; - const struct hantro_variant *variant; - struct delayed_work watchdog_work; -}; - -/** - * struct hantro_ctx - Context (instance) private data. - * - * @dev: VPU driver data to which the context belongs. - * @fh: V4L2 file handler. - * @is_encoder: Decoder or encoder context? - * - * @sequence_cap: Sequence counter for capture queue - * @sequence_out: Sequence counter for output queue - * - * @vpu_src_fmt: Descriptor of active source format. - * @src_fmt: V4L2 pixel format of active source format. - * @vpu_dst_fmt: Descriptor of active destination format. - * @dst_fmt: V4L2 pixel format of active destination format. - * - * @ctrl_handler: Control handler used to register controls. - * @jpeg_quality: User-specified JPEG compression quality. - * @bit_depth: Bit depth of current frame - * - * @codec_ops: Set of operations related to codec mode. - * @postproc: Post-processing context. - * @h264_dec: H.264-decoding context. - * @jpeg_enc: JPEG-encoding context. - * @mpeg2_dec: MPEG-2-decoding context. - * @vp8_dec: VP8-decoding context. - * @hevc_dec: HEVC-decoding context. - * @vp9_dec: VP9-decoding context. - */ -struct hantro_ctx { - struct hantro_dev *dev; - struct v4l2_fh fh; - bool is_encoder; - - u32 sequence_cap; - u32 sequence_out; - - const struct hantro_fmt *vpu_src_fmt; - struct v4l2_pix_format_mplane src_fmt; - const struct hantro_fmt *vpu_dst_fmt; - struct v4l2_pix_format_mplane dst_fmt; - - struct v4l2_ctrl_handler ctrl_handler; - int jpeg_quality; - int bit_depth; - - const struct hantro_codec_ops *codec_ops; - struct hantro_postproc_ctx postproc; - - /* Specific for particular codec modes. */ - union { - struct hantro_h264_dec_hw_ctx h264_dec; - struct hantro_mpeg2_dec_hw_ctx mpeg2_dec; - struct hantro_vp8_dec_hw_ctx vp8_dec; - struct hantro_hevc_dec_hw_ctx hevc_dec; - struct hantro_vp9_dec_hw_ctx vp9_dec; - }; -}; - -/** - * struct hantro_fmt - information about supported video formats. - * @name: Human readable name of the format. - * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. - * @codec_mode: Codec mode related to this format. See - * enum hantro_codec_mode. - * @header_size: Optional header size. Currently used by JPEG encoder. - * @max_depth: Maximum depth, for bitstream formats - * @enc_fmt: Format identifier for encoder registers. - * @frmsize: Supported range of frame sizes (only for bitstream formats). - * @postprocessed: Indicates if this format needs the post-processor. - * @match_depth: Indicates if format bit depth must match video bit depth - */ -struct hantro_fmt { - char *name; - u32 fourcc; - enum hantro_codec_mode codec_mode; - int header_size; - int max_depth; - enum hantro_enc_fmt enc_fmt; - struct v4l2_frmsize_stepwise frmsize; - bool postprocessed; - bool match_depth; -}; - -struct hantro_reg { - u32 base; - u32 shift; - u32 mask; -}; - -struct hantro_postproc_regs { - struct hantro_reg pipeline_en; - struct hantro_reg max_burst; - struct hantro_reg clk_gate; - struct hantro_reg out_swap32; - struct hantro_reg out_endian; - struct hantro_reg out_luma_base; - struct hantro_reg input_width; - struct hantro_reg input_height; - struct hantro_reg output_width; - struct hantro_reg output_height; - struct hantro_reg input_fmt; - struct hantro_reg output_fmt; - struct hantro_reg orig_width; - struct hantro_reg display_width; -}; - -struct hantro_vp9_decoded_buffer_info { - /* Info needed when the decoded frame serves as a reference frame. */ - unsigned short width; - unsigned short height; - u32 bit_depth : 4; -}; - -struct hantro_decoded_buffer { - /* Must be the first field in this struct. */ - struct v4l2_m2m_buffer base; - - union { - struct hantro_vp9_decoded_buffer_info vp9; - }; -}; - -/* Logging helpers */ - -/** - * DOC: hantro_debug: Module parameter to control level of debugging messages. - * - * Level of debugging messages can be controlled by bits of - * module parameter called "debug". Meaning of particular - * bits is as follows: - * - * bit 0 - global information: mode, size, init, release - * bit 1 - each run start/result information - * bit 2 - contents of small controls from userspace - * bit 3 - contents of big controls from userspace - * bit 4 - detail fmt, ctrl, buffer q/dq information - * bit 5 - detail function enter/leave trace information - * bit 6 - register write/read information - */ -extern int hantro_debug; - -#define vpu_debug(level, fmt, args...) \ - do { \ - if (hantro_debug & BIT(level)) \ - pr_info("%s:%d: " fmt, \ - __func__, __LINE__, ##args); \ - } while (0) - -#define vpu_err(fmt, args...) \ - pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) - -/* Structure access helpers. */ -static inline struct hantro_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct hantro_ctx, fh); -} - -/* Register accessors. */ -static inline void vepu_write_relaxed(struct hantro_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel_relaxed(val, vpu->enc_base + reg); -} - -static inline void vepu_write(struct hantro_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel(val, vpu->enc_base + reg); -} - -static inline u32 vepu_read(struct hantro_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->enc_base + reg); - - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - return val; -} - -static inline void vdpu_write_relaxed(struct hantro_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel_relaxed(val, vpu->dec_base + reg); -} - -static inline void vdpu_write(struct hantro_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel(val, vpu->dec_base + reg); -} - -static inline void hantro_write_addr(struct hantro_dev *vpu, - unsigned long offset, - dma_addr_t addr) -{ - vdpu_write(vpu, addr & 0xffffffff, offset); -} - -static inline u32 vdpu_read(struct hantro_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->dec_base + reg); - - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - return val; -} - -static inline u32 vdpu_read_mask(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - u32 v; - - v = vdpu_read(vpu, reg->base); - v &= ~(reg->mask << reg->shift); - v |= ((val & reg->mask) << reg->shift); - return v; -} - -static inline void hantro_reg_write(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - vdpu_write_relaxed(vpu, vdpu_read_mask(vpu, reg, val), reg->base); -} - -static inline void hantro_reg_write_s(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); -} - -void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); -dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); - -static inline struct vb2_v4l2_buffer * -hantro_get_src_buf(struct hantro_ctx *ctx) -{ - return v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); -} - -static inline struct vb2_v4l2_buffer * -hantro_get_dst_buf(struct hantro_ctx *ctx) -{ - return v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); -} - -bool hantro_needs_postproc(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt); - -static inline dma_addr_t -hantro_get_dec_buf_addr(struct hantro_ctx *ctx, struct vb2_buffer *vb) -{ - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - return ctx->postproc.dec_q[vb->index].dma; - return vb2_dma_contig_plane_dma_addr(vb, 0); -} - -static inline struct hantro_decoded_buffer * -vb2_to_hantro_decoded_buf(struct vb2_buffer *buf) -{ - return container_of(buf, struct hantro_decoded_buffer, base.vb.vb2_buf); -} - -void hantro_postproc_disable(struct hantro_ctx *ctx); -void hantro_postproc_enable(struct hantro_ctx *ctx); -void hantro_postproc_free(struct hantro_ctx *ctx); -int hantro_postproc_alloc(struct hantro_ctx *ctx); -int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize); - -#endif /* HANTRO_H_ */ diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c deleted file mode 100644 index 2036f72eeb4a..000000000000 --- a/drivers/staging/media/hantro/hantro_drv.c +++ /dev/null @@ -1,1146 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Collabora, Ltd. - * Copyright 2018 Google LLC. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hantro_v4l2.h" -#include "hantro.h" -#include "hantro_hw.h" - -#define DRIVER_NAME "hantro-vpu" - -int hantro_debug; -module_param_named(debug, hantro_debug, int, 0644); -MODULE_PARM_DESC(debug, - "Debug level - higher value produces more verbose messages"); - -void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id) -{ - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, id); - return ctrl ? ctrl->p_cur.p : NULL; -} - -dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) -{ - struct vb2_queue *q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); - struct vb2_buffer *buf; - - buf = vb2_find_buffer(q, ts); - if (!buf) - return 0; - return hantro_get_dec_buf_addr(ctx, buf); -} - -static const struct v4l2_event hantro_eos_event = { - .type = V4L2_EVENT_EOS -}; - -static void hantro_job_finish_no_pm(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - enum vb2_buffer_state result) -{ - struct vb2_v4l2_buffer *src, *dst; - - src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - if (WARN_ON(!src)) - return; - if (WARN_ON(!dst)) - return; - - src->sequence = ctx->sequence_out++; - dst->sequence = ctx->sequence_cap++; - - if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src)) { - dst->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); - } - - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, - result); -} - -static void hantro_job_finish(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - enum vb2_buffer_state result) -{ - pm_runtime_mark_last_busy(vpu->dev); - pm_runtime_put_autosuspend(vpu->dev); - - clk_bulk_disable(vpu->variant->num_clocks, vpu->clocks); - - hantro_job_finish_no_pm(vpu, ctx, result); -} - -void hantro_irq_done(struct hantro_dev *vpu, - enum vb2_buffer_state result) -{ - struct hantro_ctx *ctx = - v4l2_m2m_get_curr_priv(vpu->m2m_dev); - - /* - * If cancel_delayed_work returns false - * the timeout expired. The watchdog is running, - * and will take care of finishing the job. - */ - if (cancel_delayed_work(&vpu->watchdog_work)) { - if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) - ctx->codec_ops->done(ctx); - hantro_job_finish(vpu, ctx, result); - } -} - -void hantro_watchdog(struct work_struct *work) -{ - struct hantro_dev *vpu; - struct hantro_ctx *ctx; - - vpu = container_of(to_delayed_work(work), - struct hantro_dev, watchdog_work); - ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev); - if (ctx) { - vpu_err("frame processing timed out!\n"); - ctx->codec_ops->reset(ctx); - hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); - } -} - -void hantro_start_prepare_run(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - - src_buf = hantro_get_src_buf(ctx); - v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - - if (!ctx->is_encoder && !ctx->dev->variant->late_postproc) { - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); - } -} - -void hantro_end_prepare_run(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - - if (!ctx->is_encoder && ctx->dev->variant->late_postproc) { - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); - } - - src_buf = hantro_get_src_buf(ctx); - v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - - /* Kick the watchdog. */ - schedule_delayed_work(&ctx->dev->watchdog_work, - msecs_to_jiffies(2000)); -} - -static void device_run(void *priv) -{ - struct hantro_ctx *ctx = priv; - struct vb2_v4l2_buffer *src, *dst; - int ret; - - src = hantro_get_src_buf(ctx); - dst = hantro_get_dst_buf(ctx); - - ret = pm_runtime_resume_and_get(ctx->dev->dev); - if (ret < 0) - goto err_cancel_job; - - ret = clk_bulk_enable(ctx->dev->variant->num_clocks, ctx->dev->clocks); - if (ret) - goto err_cancel_job; - - v4l2_m2m_buf_copy_metadata(src, dst, true); - - if (ctx->codec_ops->run(ctx)) - goto err_cancel_job; - - return; - -err_cancel_job: - hantro_job_finish_no_pm(ctx->dev, ctx, VB2_BUF_STATE_ERROR); -} - -static const struct v4l2_m2m_ops vpu_m2m_ops = { - .device_run = device_run, -}; - -static int -queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) -{ - struct hantro_ctx *ctx = priv; - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_DMABUF; - src_vq->drv_priv = ctx; - src_vq->ops = &hantro_queue_ops; - src_vq->mem_ops = &vb2_dma_contig_memops; - - /* - * Driver does mostly sequential access, so sacrifice TLB efficiency - * for faster allocation. Also, no CPU access on the source queue, - * so no kernel mapping needed. - */ - src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | - DMA_ATTR_NO_KERNEL_MAPPING; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->vpu_mutex; - src_vq->dev = ctx->dev->v4l2_dev.dev; - src_vq->supports_requests = true; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->bidirectional = true; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; - /* - * The Kernel needs access to the JPEG destination buffer for the - * JPEG encoder to fill in the JPEG headers. - */ - if (!ctx->is_encoder) - dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; - dst_vq->drv_priv = ctx; - dst_vq->ops = &hantro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct hantro_decoded_buffer); - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->vpu_mutex; - dst_vq->dev = ctx->dev->v4l2_dev.dev; - - return vb2_queue_init(dst_vq); -} - -static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { - const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; - - if (sps->chroma_format_idc > 1) - /* Only 4:0:0 and 4:2:0 are supported */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ - return -EINVAL; - } else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) { - const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; - - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ - return -EINVAL; - } else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) { - const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame; - - /* We only support profile 0 */ - if (dec_params->profile != 0) - return -EINVAL; - } - return 0; -} - -static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct hantro_ctx *ctx; - - ctx = container_of(ctrl->handler, - struct hantro_ctx, ctrl_handler); - - vpu_debug(1, "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->jpeg_quality = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hantro_vp9_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct hantro_ctx *ctx; - - ctx = container_of(ctrl->handler, - struct hantro_ctx, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_STATELESS_VP9_FRAME: - ctx->bit_depth = ctrl->p_new.p_vp9_frame->bit_depth; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops hantro_ctrl_ops = { - .try_ctrl = hantro_try_ctrl, -}; - -static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { - .s_ctrl = hantro_jpeg_s_ctrl, -}; - -static const struct v4l2_ctrl_ops hantro_vp9_ctrl_ops = { - .s_ctrl = hantro_vp9_s_ctrl, -}; - -#define HANTRO_JPEG_ACTIVE_MARKERS (V4L2_JPEG_ACTIVE_MARKER_APP0 | \ - V4L2_JPEG_ACTIVE_MARKER_COM | \ - V4L2_JPEG_ACTIVE_MARKER_DQT | \ - V4L2_JPEG_ACTIVE_MARKER_DHT) - -static const struct hantro_ctrl controls[] = { - { - .codec = HANTRO_JPEG_ENCODER, - .cfg = { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .min = 5, - .max = 100, - .step = 1, - .def = 50, - .ops = &hantro_jpeg_ctrl_ops, - }, - }, { - .codec = HANTRO_JPEG_ENCODER, - .cfg = { - .id = V4L2_CID_JPEG_ACTIVE_MARKER, - .max = HANTRO_JPEG_ACTIVE_MARKERS, - .def = HANTRO_JPEG_ACTIVE_MARKERS, - /* - * Changing the set of active markers/segments also - * messes up the alignment of the JPEG header, which - * is needed to allow the hardware to write directly - * to the output buffer. Implementing this introduces - * a lot of complexity for little gain, as the markers - * enabled is already the minimum required set. - */ - .flags = V4L2_CTRL_FLAG_READ_ONLY, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_PICTURE, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, - }, - }, { - .codec = HANTRO_VP8_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP8_FRAME, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_SPS, - .ops = &hantro_ctrl_ops, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_PPS, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_MODE, - .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_START_CODE, - .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, - .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - .menu_skip_mask = - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), - .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, - } - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, - .min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - .max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - .def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_START_CODE, - .min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - .max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - .def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, - .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, - .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, - .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, - .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_SPS, - .ops = &hantro_ctrl_ops, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_PPS, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, - }, - }, { - .codec = HANTRO_VP9_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP9_FRAME, - .ops = &hantro_vp9_ctrl_ops, - }, - }, { - .codec = HANTRO_VP9_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, - }, - }, -}; - -static int hantro_ctrls_setup(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - int allowed_codecs) -{ - int i, num_ctrls = ARRAY_SIZE(controls); - - v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls); - - for (i = 0; i < num_ctrls; i++) { - if (!(allowed_codecs & controls[i].codec)) - continue; - - v4l2_ctrl_new_custom(&ctx->ctrl_handler, - &controls[i].cfg, NULL); - if (ctx->ctrl_handler.error) { - vpu_err("Adding control (%d) failed %d\n", - controls[i].cfg.id, - ctx->ctrl_handler.error); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - return ctx->ctrl_handler.error; - } - } - return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); -} - -/* - * V4L2 file operations. - */ - -static int hantro_open(struct file *filp) -{ - struct hantro_dev *vpu = video_drvdata(filp); - struct video_device *vdev = video_devdata(filp); - struct hantro_func *func = hantro_vdev_to_func(vdev); - struct hantro_ctx *ctx; - int allowed_codecs, ret; - - /* - * We do not need any extra locking here, because we operate only - * on local data here, except reading few fields from dev, which - * do not change through device's lifetime (which is guaranteed by - * reference on module from open()) and V4L2 internal objects (such - * as vdev and ctx->fh), which have proper locking done in respective - * helper functions used here. - */ - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = vpu; - if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { - allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; - ctx->is_encoder = true; - } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { - allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; - ctx->is_encoder = false; - } else { - ret = -ENODEV; - goto err_ctx_free; - } - - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - goto err_ctx_free; - } - - v4l2_fh_init(&ctx->fh, vdev); - filp->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - hantro_reset_fmts(ctx); - - ret = hantro_ctrls_setup(vpu, ctx, allowed_codecs); - if (ret) { - vpu_err("Failed to set up controls\n"); - goto err_fh_free; - } - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - - return 0; - -err_fh_free: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); -err_ctx_free: - kfree(ctx); - return ret; -} - -static int hantro_release(struct file *filp) -{ - struct hantro_ctx *ctx = - container_of(filp->private_data, struct hantro_ctx, fh); - - /* - * No need for extra locking because this was the last reference - * to this file. - */ - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations hantro_fops = { - .owner = THIS_MODULE, - .open = hantro_open, - .release = hantro_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static const struct of_device_id of_hantro_match[] = { -#ifdef CONFIG_VIDEO_HANTRO_ROCKCHIP - { .compatible = "rockchip,px30-vpu", .data = &px30_vpu_variant, }, - { .compatible = "rockchip,rk3036-vpu", .data = &rk3036_vpu_variant, }, - { .compatible = "rockchip,rk3066-vpu", .data = &rk3066_vpu_variant, }, - { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, }, - { .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, }, - { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, - { .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, }, - { .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_IMX8M - { .compatible = "nxp,imx8mm-vpu-g1", .data = &imx8mm_vpu_g1_variant, }, - { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, - { .compatible = "nxp,imx8mq-vpu-g1", .data = &imx8mq_vpu_g1_variant }, - { .compatible = "nxp,imx8mq-vpu-g2", .data = &imx8mq_vpu_g2_variant }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_SAMA5D4 - { .compatible = "microchip,sama5d4-vdec", .data = &sama5d4_vdec_variant, }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_SUNXI - { .compatible = "allwinner,sun50i-h6-vpu-g2", .data = &sunxi_vpu_variant, }, -#endif - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, of_hantro_match); - -static int hantro_register_entity(struct media_device *mdev, - struct media_entity *entity, - const char *entity_name, - struct media_pad *pads, int num_pads, - int function, struct video_device *vdev) -{ - char *name; - int ret; - - entity->obj_type = MEDIA_ENTITY_TYPE_BASE; - if (function == MEDIA_ENT_F_IO_V4L) { - entity->info.dev.major = VIDEO_MAJOR; - entity->info.dev.minor = vdev->minor; - } - - name = devm_kasprintf(mdev->dev, GFP_KERNEL, "%s-%s", vdev->name, - entity_name); - if (!name) - return -ENOMEM; - - entity->name = name; - entity->function = function; - - ret = media_entity_pads_init(entity, num_pads, pads); - if (ret) - return ret; - - ret = media_device_register_entity(mdev, entity); - if (ret) - return ret; - - return 0; -} - -static int hantro_attach_func(struct hantro_dev *vpu, - struct hantro_func *func) -{ - struct media_device *mdev = &vpu->mdev; - struct media_link *link; - int ret; - - /* Create the three encoder entities with their pads */ - func->source_pad.flags = MEDIA_PAD_FL_SOURCE; - ret = hantro_register_entity(mdev, &func->vdev.entity, "source", - &func->source_pad, 1, MEDIA_ENT_F_IO_V4L, - &func->vdev); - if (ret) - return ret; - - func->proc_pads[0].flags = MEDIA_PAD_FL_SINK; - func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE; - ret = hantro_register_entity(mdev, &func->proc, "proc", - func->proc_pads, 2, func->id, - &func->vdev); - if (ret) - goto err_rel_entity0; - - func->sink_pad.flags = MEDIA_PAD_FL_SINK; - ret = hantro_register_entity(mdev, &func->sink, "sink", - &func->sink_pad, 1, MEDIA_ENT_F_IO_V4L, - &func->vdev); - if (ret) - goto err_rel_entity1; - - /* Connect the three entities */ - ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto err_rel_entity2; - - ret = media_create_pad_link(&func->proc, 1, &func->sink, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto err_rm_links0; - - /* Create video interface */ - func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO, - 0, VIDEO_MAJOR, - func->vdev.minor); - if (!func->intf_devnode) { - ret = -ENOMEM; - goto err_rm_links1; - } - - /* Connect the two DMA engines to the interface */ - link = media_create_intf_link(&func->vdev.entity, - &func->intf_devnode->intf, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (!link) { - ret = -ENOMEM; - goto err_rm_devnode; - } - - link = media_create_intf_link(&func->sink, &func->intf_devnode->intf, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (!link) { - ret = -ENOMEM; - goto err_rm_devnode; - } - return 0; - -err_rm_devnode: - media_devnode_remove(func->intf_devnode); - -err_rm_links1: - media_entity_remove_links(&func->sink); - -err_rm_links0: - media_entity_remove_links(&func->proc); - media_entity_remove_links(&func->vdev.entity); - -err_rel_entity2: - media_device_unregister_entity(&func->sink); - -err_rel_entity1: - media_device_unregister_entity(&func->proc); - -err_rel_entity0: - media_device_unregister_entity(&func->vdev.entity); - return ret; -} - -static void hantro_detach_func(struct hantro_func *func) -{ - media_devnode_remove(func->intf_devnode); - media_entity_remove_links(&func->sink); - media_entity_remove_links(&func->proc); - media_entity_remove_links(&func->vdev.entity); - media_device_unregister_entity(&func->sink); - media_device_unregister_entity(&func->proc); - media_device_unregister_entity(&func->vdev.entity); -} - -static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) -{ - const struct of_device_id *match; - struct hantro_func *func; - struct video_device *vfd; - int ret; - - match = of_match_node(of_hantro_match, vpu->dev->of_node); - func = devm_kzalloc(vpu->dev, sizeof(*func), GFP_KERNEL); - if (!func) { - v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); - return -ENOMEM; - } - - func->id = funcid; - - vfd = &func->vdev; - vfd->fops = &hantro_fops; - vfd->release = video_device_release_empty; - vfd->lock = &vpu->vpu_mutex; - vfd->v4l2_dev = &vpu->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - vfd->ioctl_ops = &hantro_ioctl_ops; - snprintf(vfd->name, sizeof(vfd->name), "%s-%s", match->compatible, - funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER ? "enc" : "dec"); - - if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { - vpu->encoder = func; - } else { - vpu->decoder = func; - v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); - v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); - } - - video_set_drvdata(vfd, vpu); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); - return ret; - } - - ret = hantro_attach_func(vpu, func); - if (ret) { - v4l2_err(&vpu->v4l2_dev, - "Failed to attach functionality to the media device\n"); - goto err_unreg_dev; - } - - v4l2_info(&vpu->v4l2_dev, "registered %s as /dev/video%d\n", vfd->name, - vfd->num); - - return 0; - -err_unreg_dev: - video_unregister_device(vfd); - return ret; -} - -static int hantro_add_enc_func(struct hantro_dev *vpu) -{ - if (!vpu->variant->enc_fmts) - return 0; - - return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); -} - -static int hantro_add_dec_func(struct hantro_dev *vpu) -{ - if (!vpu->variant->dec_fmts) - return 0; - - return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); -} - -static void hantro_remove_func(struct hantro_dev *vpu, - unsigned int funcid) -{ - struct hantro_func *func; - - if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) - func = vpu->encoder; - else - func = vpu->decoder; - - if (!func) - return; - - hantro_detach_func(func); - video_unregister_device(&func->vdev); -} - -static void hantro_remove_enc_func(struct hantro_dev *vpu) -{ - hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); -} - -static void hantro_remove_dec_func(struct hantro_dev *vpu) -{ - hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); -} - -static const struct media_device_ops hantro_m2m_media_ops = { - .req_validate = vb2_request_validate, - .req_queue = v4l2_m2m_request_queue, -}; - -static int hantro_probe(struct platform_device *pdev) -{ - const struct of_device_id *match; - struct hantro_dev *vpu; - struct resource *res; - int num_bases; - int i, ret; - - vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); - if (!vpu) - return -ENOMEM; - - vpu->dev = &pdev->dev; - vpu->pdev = pdev; - mutex_init(&vpu->vpu_mutex); - spin_lock_init(&vpu->irqlock); - - match = of_match_node(of_hantro_match, pdev->dev.of_node); - vpu->variant = match->data; - - /* - * Support for nxp,imx8mq-vpu is kept for backwards compatibility - * but it's deprecated. Please update your DTS file to use - * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead. - */ - if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu")) - dev_warn(&pdev->dev, "%s compatible is deprecated\n", - match->compatible); - - INIT_DELAYED_WORK(&vpu->watchdog_work, hantro_watchdog); - - vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks, - sizeof(*vpu->clocks), GFP_KERNEL); - if (!vpu->clocks) - return -ENOMEM; - - if (vpu->variant->num_clocks > 1) { - for (i = 0; i < vpu->variant->num_clocks; i++) - vpu->clocks[i].id = vpu->variant->clk_names[i]; - - ret = devm_clk_bulk_get(&pdev->dev, vpu->variant->num_clocks, - vpu->clocks); - if (ret) - return ret; - } else { - /* - * If the driver has a single clk, chances are there will be no - * actual name in the DT bindings. - */ - vpu->clocks[0].clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(vpu->clocks[0].clk)) - return PTR_ERR(vpu->clocks[0].clk); - } - - vpu->resets = devm_reset_control_array_get(&pdev->dev, false, true); - if (IS_ERR(vpu->resets)) - return PTR_ERR(vpu->resets); - - num_bases = vpu->variant->num_regs ?: 1; - vpu->reg_bases = devm_kcalloc(&pdev->dev, num_bases, - sizeof(*vpu->reg_bases), GFP_KERNEL); - if (!vpu->reg_bases) - return -ENOMEM; - - for (i = 0; i < num_bases; i++) { - res = vpu->variant->reg_names ? - platform_get_resource_byname(vpu->pdev, IORESOURCE_MEM, - vpu->variant->reg_names[i]) : - platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); - vpu->reg_bases[i] = devm_ioremap_resource(vpu->dev, res); - if (IS_ERR(vpu->reg_bases[i])) - return PTR_ERR(vpu->reg_bases[i]); - } - vpu->enc_base = vpu->reg_bases[0] + vpu->variant->enc_offset; - vpu->dec_base = vpu->reg_bases[0] + vpu->variant->dec_offset; - - /** - * TODO: Eventually allow taking advantage of full 64-bit address space. - * Until then we assume the MSB portion of buffers' base addresses is - * always 0 due to this masking operation. - */ - ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(vpu->dev, "Could not set DMA coherent mask.\n"); - return ret; - } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - - for (i = 0; i < vpu->variant->num_irqs; i++) { - const char *irq_name; - int irq; - - if (!vpu->variant->irqs[i].handler) - continue; - - if (vpu->variant->num_irqs > 1) { - irq_name = vpu->variant->irqs[i].name; - irq = platform_get_irq_byname(vpu->pdev, irq_name); - } else { - /* - * If the driver has a single IRQ, chances are there - * will be no actual name in the DT bindings. - */ - irq_name = "default"; - irq = platform_get_irq(vpu->pdev, 0); - } - if (irq <= 0) - return -ENXIO; - - ret = devm_request_irq(vpu->dev, irq, - vpu->variant->irqs[i].handler, 0, - dev_name(vpu->dev), vpu); - if (ret) { - dev_err(vpu->dev, "Could not request %s IRQ.\n", - irq_name); - return ret; - } - } - - if (vpu->variant->init) { - ret = vpu->variant->init(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to init VPU hardware\n"); - return ret; - } - } - - pm_runtime_set_autosuspend_delay(vpu->dev, 100); - pm_runtime_use_autosuspend(vpu->dev); - pm_runtime_enable(vpu->dev); - - ret = reset_control_deassert(vpu->resets); - if (ret) { - dev_err(&pdev->dev, "Failed to deassert resets\n"); - goto err_pm_disable; - } - - ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks); - if (ret) { - dev_err(&pdev->dev, "Failed to prepare clocks\n"); - goto err_rst_assert; - } - - ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - goto err_clk_unprepare; - } - platform_set_drvdata(pdev, vpu); - - vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops); - if (IS_ERR(vpu->m2m_dev)) { - v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n"); - ret = PTR_ERR(vpu->m2m_dev); - goto err_v4l2_unreg; - } - - vpu->mdev.dev = vpu->dev; - strscpy(vpu->mdev.model, DRIVER_NAME, sizeof(vpu->mdev.model)); - strscpy(vpu->mdev.bus_info, "platform: " DRIVER_NAME, - sizeof(vpu->mdev.bus_info)); - media_device_init(&vpu->mdev); - vpu->mdev.ops = &hantro_m2m_media_ops; - vpu->v4l2_dev.mdev = &vpu->mdev; - - ret = hantro_add_enc_func(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to register encoder\n"); - goto err_m2m_rel; - } - - ret = hantro_add_dec_func(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to register decoder\n"); - goto err_rm_enc_func; - } - - ret = media_device_register(&vpu->mdev); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n"); - goto err_rm_dec_func; - } - - return 0; - -err_rm_dec_func: - hantro_remove_dec_func(vpu); -err_rm_enc_func: - hantro_remove_enc_func(vpu); -err_m2m_rel: - media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); -err_v4l2_unreg: - v4l2_device_unregister(&vpu->v4l2_dev); -err_clk_unprepare: - clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); -err_rst_assert: - reset_control_assert(vpu->resets); -err_pm_disable: - pm_runtime_dont_use_autosuspend(vpu->dev); - pm_runtime_disable(vpu->dev); - return ret; -} - -static int hantro_remove(struct platform_device *pdev) -{ - struct hantro_dev *vpu = platform_get_drvdata(pdev); - - v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); - - media_device_unregister(&vpu->mdev); - hantro_remove_dec_func(vpu); - hantro_remove_enc_func(vpu); - media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); - v4l2_device_unregister(&vpu->v4l2_dev); - clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); - reset_control_assert(vpu->resets); - pm_runtime_dont_use_autosuspend(vpu->dev); - pm_runtime_disable(vpu->dev); - return 0; -} - -#ifdef CONFIG_PM -static int hantro_runtime_resume(struct device *dev) -{ - struct hantro_dev *vpu = dev_get_drvdata(dev); - - if (vpu->variant->runtime_resume) - return vpu->variant->runtime_resume(vpu); - - return 0; -} -#endif - -static const struct dev_pm_ops hantro_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(NULL, hantro_runtime_resume, NULL) -}; - -static struct platform_driver hantro_driver = { - .probe = hantro_probe, - .remove = hantro_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(of_hantro_match), - .pm = &hantro_pm_ops, - }, -}; -module_platform_driver(hantro_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Alpha Lin "); -MODULE_AUTHOR("Tomasz Figa "); -MODULE_AUTHOR("Ezequiel Garcia "); -MODULE_DESCRIPTION("Hantro VPU codec driver"); diff --git a/drivers/staging/media/hantro/hantro_g1.c b/drivers/staging/media/hantro/hantro_g1.c deleted file mode 100644 index 0ab1cee62218..000000000000 --- a/drivers/staging/media/hantro/hantro_g1.c +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Jeffy Chen - * Copyright (C) 2019 Pengutronix, Philipp Zabel - * Copyright (C) 2021 Collabora Ltd, Emil Velikov - */ - -#include "hantro.h" -#include "hantro_g1_regs.h" - -irqreturn_t hantro_g1_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, G1_REG_INTERRUPT); - state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -void hantro_g1_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - vdpu_write(vpu, 1, G1_REG_SOFT_RESET); -} diff --git a/drivers/staging/media/hantro/hantro_g1_h264_dec.c b/drivers/staging/media/hantro/hantro_g1_h264_dec.c deleted file mode 100644 index 9de7f05eff2a..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_h264_dec.c +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong - * Herman Chen - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - */ - -#include -#include - -#include - -#include "hantro_g1_regs.h" -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - - /* Decoder control register 0. */ - reg = G1_REG_DEC_CTRL0_DEC_AXI_AUTO; - if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) - reg |= G1_REG_DEC_CTRL0_SEQ_MBAFF_E; - if (sps->profile_idc > 66) { - reg |= G1_REG_DEC_CTRL0_PICORD_COUNT_E; - if (dec_param->nal_ref_idc) - reg |= G1_REG_DEC_CTRL0_WRITE_MVS_E; - } - - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && - (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || - dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) - reg |= G1_REG_DEC_CTRL0_PIC_INTERLACE_E; - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) - reg |= G1_REG_DEC_CTRL0_PIC_FIELDMODE_E; - if (!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) - reg |= G1_REG_DEC_CTRL0_PIC_TOPFIELD_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); - - /* Decoder control register 1. */ - reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) | - G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | - G1_REG_DEC_CTRL1_REF_FRAMES(sps->max_num_ref_frames); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); - - /* Decoder control register 2. */ - reg = G1_REG_DEC_CTRL2_CH_QP_OFFSET(pps->chroma_qp_index_offset) | - G1_REG_DEC_CTRL2_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset); - - if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) - reg |= G1_REG_DEC_CTRL2_TYPE1_QUANT_E; - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) - reg |= G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); - - /* Decoder control register 3. */ - reg = G1_REG_DEC_CTRL3_START_CODE_E | - G1_REG_DEC_CTRL3_INIT_QP(pps->pic_init_qp_minus26 + 26) | - G1_REG_DEC_CTRL3_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL3); - - /* Decoder control register 4. */ - reg = G1_REG_DEC_CTRL4_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | - G1_REG_DEC_CTRL4_FRAMENUM(dec_param->frame_num) | - G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc); - if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) - reg |= G1_REG_DEC_CTRL4_CABAC_E; - if (sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) - reg |= G1_REG_DEC_CTRL4_DIR_8X8_INFER_E; - if (sps->profile_idc >= 100 && sps->chroma_format_idc == 0) - reg |= G1_REG_DEC_CTRL4_BLACKWHITE_E; - if (pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) - reg |= G1_REG_DEC_CTRL4_WEIGHT_PRED_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); - - /* Decoder control register 5. */ - reg = G1_REG_DEC_CTRL5_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | - G1_REG_DEC_CTRL5_IDR_PIC_ID(dec_param->idr_pic_id); - if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) - reg |= G1_REG_DEC_CTRL5_CONST_INTRA_E; - if (pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) - reg |= G1_REG_DEC_CTRL5_FILT_CTRL_PRES; - if (pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) - reg |= G1_REG_DEC_CTRL5_RDPIC_CNT_PRES; - if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) - reg |= G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E; - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) - reg |= G1_REG_DEC_CTRL5_IDR_PIC_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL5); - - /* Decoder control register 6. */ - reg = G1_REG_DEC_CTRL6_PPS_ID(pps->pic_parameter_set_id) | - G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | - G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | - G1_REG_DEC_CTRL6_POC_LENGTH(dec_param->pic_order_cnt_bit_size); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL6); - - /* Error concealment register. */ - vdpu_write_relaxed(vpu, 0, G1_REG_ERR_CONC); - - /* Prediction filter tap register. */ - vdpu_write_relaxed(vpu, - G1_REG_PRED_FLT_PRED_BC_TAP_0_0(1) | - G1_REG_PRED_FLT_PRED_BC_TAP_0_1(-5 & 0x3ff) | - G1_REG_PRED_FLT_PRED_BC_TAP_0_2(20), - G1_REG_PRED_FLT); - - /* Reference picture buffer control register. */ - vdpu_write_relaxed(vpu, 0, G1_REG_REF_BUF_CTRL); - - /* Reference picture buffer control register 2. */ - vdpu_write_relaxed(vpu, G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(8), - G1_REG_REF_BUF_CTRL2); -} - -static void set_ref(struct hantro_ctx *ctx) -{ - const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; - struct hantro_dev *vpu = ctx->dev; - int reg_num; - u32 reg; - int i; - - vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_valid, G1_REG_VALID_REF); - vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_longterm, G1_REG_LT_REF); - - /* - * Set up reference frame picture numbers. - * - * Each G1_REG_REF_PIC(x) register contains numbers of two - * subsequential reference pictures. - */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i += 2) { - reg = G1_REG_REF_PIC_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, i)) | - G1_REG_REF_PIC_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, i + 1)); - vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(i / 2)); - } - - b0_reflist = ctx->h264_dec.reflists.b0; - b1_reflist = ctx->h264_dec.reflists.b1; - p_reflist = ctx->h264_dec.reflists.p; - - /* - * Each G1_REG_BD_REF_PIC(x) register contains three entries - * of each forward and backward picture list. - */ - reg_num = 0; - for (i = 0; i < 15; i += 3) { - reg = G1_REG_BD_REF_PIC_BINIT_RLIST_F0(b0_reflist[i].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_F1(b0_reflist[i + 1].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_F2(b0_reflist[i + 2].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B0(b1_reflist[i].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B1(b1_reflist[i + 1].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B2(b1_reflist[i + 2].index); - vdpu_write_relaxed(vpu, reg, G1_REG_BD_REF_PIC(reg_num++)); - } - - /* - * G1_REG_BD_P_REF_PIC register contains last entries (index 15) - * of forward and backward reference picture lists and first 4 entries - * of P forward picture list. - */ - reg = G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(b0_reflist[15].index) | - G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(b1_reflist[15].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(p_reflist[0].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(p_reflist[1].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(p_reflist[2].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(p_reflist[3].index); - vdpu_write_relaxed(vpu, reg, G1_REG_BD_P_REF_PIC); - - /* - * Each G1_REG_FWD_PIC(x) register contains six consecutive - * entries of P forward picture list, starting from index 4. - */ - reg_num = 0; - for (i = 4; i < HANTRO_H264_DPB_SIZE; i += 6) { - reg = G1_REG_FWD_PIC_PINIT_RLIST_F0(p_reflist[i].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F1(p_reflist[i + 1].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F2(p_reflist[i + 2].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F3(p_reflist[i + 3].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F4(p_reflist[i + 4].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F5(p_reflist[i + 5].index); - vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(reg_num++)); - } - - /* Set up addresses of DPB buffers. */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { - dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); - - vdpu_write_relaxed(vpu, dma_addr, G1_REG_ADDR_REF(i)); - } -} - -static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - struct vb2_v4l2_buffer *dst_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma, dst_dma; - size_t offset = 0; - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - vdpu_write_relaxed(vpu, src_dma, G1_REG_ADDR_STR); - - /* Destination (decoded frame) buffer. */ - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); - /* Adjust dma addr to start at second line for bottom field */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset = ALIGN(ctx->src_fmt.width, MB_DIM); - vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DST); - - /* Higher profiles require DMV buffer appended to reference frames. */ - if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { - unsigned int bytes_per_mb = 384; - - /* DMV buffer for monochrome start directly after Y-plane */ - if (ctrls->sps->profile_idc >= 100 && - ctrls->sps->chroma_format_idc == 0) - bytes_per_mb = 256; - offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - - /* - * DMV buffer is split in two for field encoded frames, - * adjust offset for bottom field - */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset += 32 * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DIR_MV); - } - - /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ - vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, G1_REG_ADDR_QTABLE); -} - -int hantro_g1_h264_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf; - int ret; - - /* Prepare the H264 decoder context. */ - ret = hantro_h264_dec_prepare_run(ctx); - if (ret) - return ret; - - /* Configure hardware registers. */ - src_buf = hantro_get_src_buf(ctx); - set_params(ctx, src_buf); - set_ref(ctx); - set_buffers(ctx, src_buf); - - hantro_end_prepare_run(ctx); - - /* Start decoding! */ - vdpu_write_relaxed(vpu, - G1_REG_CONFIG_DEC_AXI_RD_ID(0xffu) | - G1_REG_CONFIG_DEC_TIMEOUT_E | - G1_REG_CONFIG_DEC_OUT_ENDIAN | - G1_REG_CONFIG_DEC_STRENDIAN_E | - G1_REG_CONFIG_DEC_MAX_BURST(16) | - G1_REG_CONFIG_DEC_OUTSWAP32_E | - G1_REG_CONFIG_DEC_INSWAP32_E | - G1_REG_CONFIG_DEC_STRSWAP32_E | - G1_REG_CONFIG_DEC_CLK_GATE_E, - G1_REG_CONFIG); - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c b/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c deleted file mode 100644 index 9aea331e1a3c..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include -#include -#include -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_g1_regs.h" - -#define G1_SWREG(nr) ((nr) * 4) - -#define G1_REG_RLC_VLC_BASE G1_SWREG(12) -#define G1_REG_DEC_OUT_BASE G1_SWREG(13) -#define G1_REG_REFER0_BASE G1_SWREG(14) -#define G1_REG_REFER1_BASE G1_SWREG(15) -#define G1_REG_REFER2_BASE G1_SWREG(16) -#define G1_REG_REFER3_BASE G1_SWREG(17) -#define G1_REG_QTABLE_BASE G1_SWREG(40) - -#define G1_REG_DEC_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define G1_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(23) : 0) -#define G1_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(22) : 0) -#define G1_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(21) : 0) -#define G1_REG_DEC_INSWAP32_E(v) ((v) ? BIT(20) : 0) -#define G1_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(18) : 0) -#define G1_REG_DEC_LATENCY(v) (((v) << 11) & GENMASK(16, 11)) -#define G1_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(10) : 0) -#define G1_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(9) : 0) -#define G1_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(8) : 0) -#define G1_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(6) : 0) -#define G1_REG_DEC_SCMD_DIS(v) ((v) ? BIT(5) : 0) -#define G1_REG_DEC_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) - -#define G1_REG_DEC_MODE(v) (((v) << 28) & GENMASK(31, 28)) -#define G1_REG_RLC_MODE_E(v) ((v) ? BIT(27) : 0) -#define G1_REG_PIC_INTERLACE_E(v) ((v) ? BIT(23) : 0) -#define G1_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(22) : 0) -#define G1_REG_PIC_B_E(v) ((v) ? BIT(21) : 0) -#define G1_REG_PIC_INTER_E(v) ((v) ? BIT(20) : 0) -#define G1_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_FWD_INTERLACE_E(v) ((v) ? BIT(18) : 0) -#define G1_REG_FILTERING_DIS(v) ((v) ? BIT(14) : 0) -#define G1_REG_WRITE_MVS_E(v) ((v) ? BIT(12) : 0) -#define G1_REG_DEC_AXI_WR_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define G1_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) -#define G1_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) -#define G1_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) - -#define G1_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) -#define G1_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) -#define G1_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) -#define G1_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) -#define G1_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) -#define G1_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) - -#define G1_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define G1_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define G1_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) -#define G1_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) -#define G1_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) -#define G1_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) -#define G1_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) -#define G1_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) - -#define G1_REG_STARTMB_X(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_STARTMB_Y(v) (((v) << 15) & GENMASK(22, 15)) - -#define G1_REG_APF_THRESHOLD(v) (((v) << 0) & GENMASK(13, 0)) - -static void -hantro_g1_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - struct v4l2_ctrl_mpeg2_quantisation *q; - - q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); - hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); - vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, G1_REG_QTABLE_BASE); -} - -static void -hantro_g1_mpeg2_dec_set_buffers(struct hantro_dev *vpu, struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf, - const struct v4l2_ctrl_mpeg2_sequence *seq, - const struct v4l2_ctrl_mpeg2_picture *pic) -{ - dma_addr_t forward_addr = 0, backward_addr = 0; - dma_addr_t current_addr, addr; - - switch (pic->picture_coding_type) { - case V4L2_MPEG2_PIC_CODING_TYPE_B: - backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); - fallthrough; - case V4L2_MPEG2_PIC_CODING_TYPE_P: - forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); - } - - /* Source bitstream buffer */ - addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vdpu_write_relaxed(vpu, addr, G1_REG_RLC_VLC_BASE); - - /* Destination frame buffer */ - addr = hantro_get_dec_buf_addr(ctx, dst_buf); - current_addr = addr; - - if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) - addr += ALIGN(ctx->dst_fmt.width, 16); - vdpu_write_relaxed(vpu, addr, G1_REG_DEC_OUT_BASE); - - if (!forward_addr) - forward_addr = current_addr; - if (!backward_addr) - backward_addr = current_addr; - - /* Set forward ref frame (top/bottom field) */ - if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || - pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || - (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && - pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST) || - (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && - !(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST))) { - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { - vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); - } - - /* Set backward ref frame (top/bottom field) */ - vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER2_BASE); - vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER3_BASE); -} - -int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - const struct v4l2_ctrl_mpeg2_sequence *seq; - const struct v4l2_ctrl_mpeg2_picture *pic; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - /* Apply request controls if any */ - hantro_start_prepare_run(ctx); - - seq = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_SEQUENCE); - pic = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_PICTURE); - - reg = G1_REG_DEC_AXI_RD_ID(0) | - G1_REG_DEC_TIMEOUT_E(1) | - G1_REG_DEC_STRSWAP32_E(1) | - G1_REG_DEC_STRENDIAN_E(1) | - G1_REG_DEC_INSWAP32_E(1) | - G1_REG_DEC_OUTSWAP32_E(1) | - G1_REG_DEC_DATA_DISC_E(0) | - G1_REG_DEC_LATENCY(0) | - G1_REG_DEC_CLK_GATE_E(1) | - G1_REG_DEC_IN_ENDIAN(1) | - G1_REG_DEC_OUT_ENDIAN(1) | - G1_REG_DEC_ADV_PRE_DIS(0) | - G1_REG_DEC_SCMD_DIS(0) | - G1_REG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, G1_SWREG(2)); - - reg = G1_REG_DEC_MODE(5) | - G1_REG_RLC_MODE_E(0) | - G1_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | - G1_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | - G1_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | - G1_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | - G1_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | - G1_REG_FWD_INTERLACE_E(0) | - G1_REG_FILTERING_DIS(1) | - G1_REG_WRITE_MVS_E(0) | - G1_REG_DEC_AXI_WR_ID(0); - vdpu_write_relaxed(vpu, reg, G1_SWREG(3)); - - reg = G1_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | - G1_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | - G1_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - G1_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); - vdpu_write_relaxed(vpu, reg, G1_SWREG(4)); - - reg = G1_REG_STRM_START_BIT(0) | - G1_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | - G1_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | - G1_REG_INTRA_DC_PREC(pic->intra_dc_precision) | - G1_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | - G1_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); - vdpu_write_relaxed(vpu, reg, G1_SWREG(5)); - - reg = G1_REG_INIT_QP(1) | - G1_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, G1_SWREG(6)); - - reg = G1_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - G1_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | - G1_REG_FCODE_FWD_VER(pic->f_code[0][1]) | - G1_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | - G1_REG_FCODE_BWD_VER(pic->f_code[1][1]) | - G1_REG_MV_ACCURACY_FWD(1) | - G1_REG_MV_ACCURACY_BWD(1); - vdpu_write_relaxed(vpu, reg, G1_SWREG(18)); - - reg = G1_REG_STARTMB_X(0) | - G1_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, G1_SWREG(48)); - - reg = G1_REG_APF_THRESHOLD(8); - vdpu_write_relaxed(vpu, reg, G1_SWREG(55)); - - hantro_g1_mpeg2_dec_set_quantisation(vpu, ctx); - hantro_g1_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf, - seq, pic); - - hantro_end_prepare_run(ctx); - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g1_regs.h b/drivers/staging/media/hantro/hantro_g1_regs.h deleted file mode 100644 index c623b3b0be18..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_regs.h +++ /dev/null @@ -1,356 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa - */ - -#ifndef HANTRO_G1_REGS_H_ -#define HANTRO_G1_REGS_H_ - -#define G1_SWREG(nr) ((nr) * 4) - -/* Decoder registers. */ -#define G1_REG_INTERRUPT 0x004 -#define G1_REG_INTERRUPT_DEC_PIC_INF BIT(24) -#define G1_REG_INTERRUPT_DEC_TIMEOUT BIT(18) -#define G1_REG_INTERRUPT_DEC_SLICE_INT BIT(17) -#define G1_REG_INTERRUPT_DEC_ERROR_INT BIT(16) -#define G1_REG_INTERRUPT_DEC_ASO_INT BIT(15) -#define G1_REG_INTERRUPT_DEC_BUFFER_INT BIT(14) -#define G1_REG_INTERRUPT_DEC_BUS_INT BIT(13) -#define G1_REG_INTERRUPT_DEC_RDY_INT BIT(12) -#define G1_REG_INTERRUPT_DEC_IRQ BIT(8) -#define G1_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) -#define G1_REG_INTERRUPT_DEC_E BIT(0) -#define G1_REG_CONFIG 0x008 -#define G1_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 24) -#define G1_REG_CONFIG_DEC_TIMEOUT_E BIT(23) -#define G1_REG_CONFIG_DEC_STRSWAP32_E BIT(22) -#define G1_REG_CONFIG_DEC_STRENDIAN_E BIT(21) -#define G1_REG_CONFIG_DEC_INSWAP32_E BIT(20) -#define G1_REG_CONFIG_DEC_OUTSWAP32_E BIT(19) -#define G1_REG_CONFIG_DEC_DATA_DISC_E BIT(18) -#define G1_REG_CONFIG_TILED_MODE_MSB BIT(17) -#define G1_REG_CONFIG_DEC_OUT_TILED_E BIT(17) -#define G1_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 11) -#define G1_REG_CONFIG_DEC_CLK_GATE_E BIT(10) -#define G1_REG_CONFIG_DEC_IN_ENDIAN BIT(9) -#define G1_REG_CONFIG_DEC_OUT_ENDIAN BIT(8) -#define G1_REG_CONFIG_PRIORITY_MODE(x) (((x) & 0x7) << 5) -#define G1_REG_CONFIG_TILED_MODE_LSB BIT(7) -#define G1_REG_CONFIG_DEC_ADV_PRE_DIS BIT(6) -#define G1_REG_CONFIG_DEC_SCMD_DIS BIT(5) -#define G1_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL0 0x00c -#define G1_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 28) -#define G1_REG_DEC_CTRL0_RLC_MODE_E BIT(27) -#define G1_REG_DEC_CTRL0_SKIP_MODE BIT(26) -#define G1_REG_DEC_CTRL0_DIVX3_E BIT(25) -#define G1_REG_DEC_CTRL0_PJPEG_E BIT(24) -#define G1_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(23) -#define G1_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(22) -#define G1_REG_DEC_CTRL0_PIC_B_E BIT(21) -#define G1_REG_DEC_CTRL0_PIC_INTER_E BIT(20) -#define G1_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(19) -#define G1_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(18) -#define G1_REG_DEC_CTRL0_SORENSON_E BIT(17) -#define G1_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(16) -#define G1_REG_DEC_CTRL0_DEC_OUT_DIS BIT(15) -#define G1_REG_DEC_CTRL0_FILTERING_DIS BIT(14) -#define G1_REG_DEC_CTRL0_WEBP_E BIT(13) -#define G1_REG_DEC_CTRL0_MVC_E BIT(13) -#define G1_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(13) -#define G1_REG_DEC_CTRL0_WRITE_MVS_E BIT(12) -#define G1_REG_DEC_CTRL0_REFTOPFIRST_E BIT(11) -#define G1_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(10) -#define G1_REG_DEC_CTRL0_PICORD_COUNT_E BIT(9) -#define G1_REG_DEC_CTRL0_DEC_AHB_HLOCK_E BIT(8) -#define G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 0) -/* Setting AXI ID to 0xff to get auto generated ID to avoid possible conflicts */ -#define G1_REG_DEC_CTRL0_DEC_AXI_AUTO G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(0xff) -#define G1_REG_DEC_CTRL1 0x010 -#define G1_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) -#define G1_REG_DEC_CTRL1_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) -#define G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) -#define G1_REG_DEC_CTRL1_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) -#define G1_REG_DEC_CTRL1_ALT_SCAN_E BIT(6) -#define G1_REG_DEC_CTRL1_TOPFIELDFIRST_E BIT(5) -#define G1_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) -#define G1_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) -#define G1_REG_DEC_CTRL1_PIC_REFER_FLAG BIT(0) -#define G1_REG_DEC_CTRL2 0x014 -#define G1_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) -#define G1_REG_DEC_CTRL2_SYNC_MARKER_E BIT(25) -#define G1_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(24) -#define G1_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) -#define G1_REG_DEC_CTRL2_INTRADC_VLC_THR(x) (((x) & 0x7) << 16) -#define G1_REG_DEC_CTRL2_VOP_TIME_INCR(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL2_DQ_PROFILE BIT(24) -#define G1_REG_DEC_CTRL2_DQBI_LEVEL BIT(23) -#define G1_REG_DEC_CTRL2_RANGE_RED_FRM_E BIT(22) -#define G1_REG_DEC_CTRL2_FAST_UVMC_E BIT(20) -#define G1_REG_DEC_CTRL2_TRANSDCTAB BIT(17) -#define G1_REG_DEC_CTRL2_TRANSACFRM(x) (((x) & 0x3) << 15) -#define G1_REG_DEC_CTRL2_TRANSACFRM2(x) (((x) & 0x3) << 13) -#define G1_REG_DEC_CTRL2_MB_MODE_TAB(x) (((x) & 0x7) << 10) -#define G1_REG_DEC_CTRL2_MVTAB(x) (((x) & 0x7) << 7) -#define G1_REG_DEC_CTRL2_CBPTAB(x) (((x) & 0x7) << 4) -#define G1_REG_DEC_CTRL2_2MV_BLK_PAT_TAB(x) (((x) & 0x3) << 2) -#define G1_REG_DEC_CTRL2_4MV_BLK_PAT_TAB(x) (((x) & 0x3) << 0) -#define G1_REG_DEC_CTRL2_QSCALE_TYPE BIT(24) -#define G1_REG_DEC_CTRL2_CON_MV_E BIT(4) -#define G1_REG_DEC_CTRL2_INTRA_DC_PREC(x) (((x) & 0x3) << 2) -#define G1_REG_DEC_CTRL2_INTRA_VLC_TAB BIT(1) -#define G1_REG_DEC_CTRL2_FRAME_PRED_DCT BIT(0) -#define G1_REG_DEC_CTRL2_JPEG_QTABLES(x) (((x) & 0x3) << 11) -#define G1_REG_DEC_CTRL2_JPEG_MODE(x) (((x) & 0x7) << 8) -#define G1_REG_DEC_CTRL2_JPEG_FILRIGHT_E BIT(7) -#define G1_REG_DEC_CTRL2_JPEG_STREAM_ALL BIT(6) -#define G1_REG_DEC_CTRL2_CR_AC_VLCTABLE BIT(5) -#define G1_REG_DEC_CTRL2_CB_AC_VLCTABLE BIT(4) -#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE BIT(3) -#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE BIT(2) -#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE3 BIT(1) -#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE3 BIT(0) -#define G1_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) -#define G1_REG_DEC_CTRL2_HUFFMAN_E BIT(17) -#define G1_REG_DEC_CTRL2_MULTISTREAM_E BIT(16) -#define G1_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) -#define G1_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL2_ALPHA_OFFSET(x) (((x) & 0x1f) << 5) -#define G1_REG_DEC_CTRL2_BETA_OFFSET(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL3 0x018 -#define G1_REG_DEC_CTRL3_START_CODE_E BIT(31) -#define G1_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) -#define G1_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(24) -#define G1_REG_DEC_CTRL3_STREAM_LEN_EXT(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) -#define G1_REG_DEC_CTRL4 0x01c -#define G1_REG_DEC_CTRL4_CABAC_E BIT(31) -#define G1_REG_DEC_CTRL4_BLACKWHITE_E BIT(30) -#define G1_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(29) -#define G1_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(28) -#define G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 26) -#define G1_REG_DEC_CTRL4_AVS_H264_H_EXT BIT(25) -#define G1_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) -#define G1_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL4_BITPLANE0_E BIT(31) -#define G1_REG_DEC_CTRL4_BITPLANE1_E BIT(30) -#define G1_REG_DEC_CTRL4_BITPLANE2_E BIT(29) -#define G1_REG_DEC_CTRL4_ALT_PQUANT(x) (((x) & 0x1f) << 24) -#define G1_REG_DEC_CTRL4_DQ_EDGES(x) (((x) & 0xf) << 20) -#define G1_REG_DEC_CTRL4_TTMBF BIT(19) -#define G1_REG_DEC_CTRL4_PQINDEX(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define G1_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define G1_REG_DEC_CTRL4_UNIQP_E BIT(11) -#define G1_REG_DEC_CTRL4_HALFQP_E BIT(10) -#define G1_REG_DEC_CTRL4_TTFRM(x) (((x) & 0x3) << 8) -#define G1_REG_DEC_CTRL4_2ND_BYTE_EMUL_E BIT(7) -#define G1_REG_DEC_CTRL4_DQUANT_E BIT(6) -#define G1_REG_DEC_CTRL4_VC1_ADV_E BIT(5) -#define G1_REG_DEC_CTRL4_PJPEG_FILDOWN_E BIT(26) -#define G1_REG_DEC_CTRL4_PJPEG_WDIV8 BIT(25) -#define G1_REG_DEC_CTRL4_PJPEG_HDIV8 BIT(24) -#define G1_REG_DEC_CTRL4_PJPEG_AH(x) (((x) & 0xf) << 20) -#define G1_REG_DEC_CTRL4_PJPEG_AL(x) (((x) & 0xf) << 16) -#define G1_REG_DEC_CTRL4_PJPEG_SS(x) (((x) & 0xff) << 8) -#define G1_REG_DEC_CTRL4_PJPEG_SE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) -#define G1_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) -#define G1_REG_DEC_CTRL4_CH_MV_RES BIT(13) -#define G1_REG_DEC_CTRL4_INIT_DC_MATCH0(x) (((x) & 0x7) << 9) -#define G1_REG_DEC_CTRL4_INIT_DC_MATCH1(x) (((x) & 0x7) << 6) -#define G1_REG_DEC_CTRL4_VP7_VERSION BIT(5) -#define G1_REG_DEC_CTRL5 0x020 -#define G1_REG_DEC_CTRL5_CONST_INTRA_E BIT(31) -#define G1_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(30) -#define G1_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(29) -#define G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(28) -#define G1_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 17) -#define G1_REG_DEC_CTRL5_IDR_PIC_E BIT(16) -#define G1_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL5_MV_SCALEFACTOR(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL5_REF_DIST_FWD(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL5_REF_DIST_BWD(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL5_LOOP_FILT_LIMIT(x) (((x) & 0xf) << 14) -#define G1_REG_DEC_CTRL5_VARIANCE_TEST_E BIT(13) -#define G1_REG_DEC_CTRL5_MV_THRESHOLD(x) (((x) & 0x7) << 10) -#define G1_REG_DEC_CTRL5_VAR_THRESHOLD(x) (((x) & 0x3ff) << 0) -#define G1_REG_DEC_CTRL5_DIVX_IDCT_E BIT(8) -#define G1_REG_DEC_CTRL5_DIVX3_SLICE_SIZE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL5_PJPEG_REST_FREQ(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL5_RV_PROFILE(x) (((x) & 0x3) << 30) -#define G1_REG_DEC_CTRL5_RV_OSV_QUANT(x) (((x) & 0x3) << 28) -#define G1_REG_DEC_CTRL5_RV_FWD_SCALE(x) (((x) & 0x3fff) << 14) -#define G1_REG_DEC_CTRL5_RV_BWD_SCALE(x) (((x) & 0x3fff) << 0) -#define G1_REG_DEC_CTRL5_INIT_DC_COMP0(x) (((x) & 0xffff) << 16) -#define G1_REG_DEC_CTRL5_INIT_DC_COMP1(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL6 0x024 -#define G1_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL6_ICOMP0_E BIT(24) -#define G1_REG_DEC_CTRL6_ISCALE0(x) (((x) & 0xff) << 16) -#define G1_REG_DEC_CTRL6_ISHIFT0(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) -#define G1_REG_DEC_CTRL6_PIC_SLICE_AM(x) (((x) & 0x1fff) << 0) -#define G1_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) -#define G1_REG_FWD_PIC(i) (0x028 + ((i) * 0x4)) -#define G1_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define G1_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define G1_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define G1_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define G1_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define G1_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define G1_REG_FWD_PIC1_ICOMP1_E BIT(24) -#define G1_REG_FWD_PIC1_ISCALE1(x) (((x) & 0xff) << 16) -#define G1_REG_FWD_PIC1_ISHIFT1(x) (((x) & 0xffff) << 0) -#define G1_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define G1_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define G1_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define G1_REG_DEC_CTRL7 0x02c -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F15(x) (((x) & 0x1f) << 25) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F14(x) (((x) & 0x1f) << 20) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F13(x) (((x) & 0x1f) << 15) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F12(x) (((x) & 0x1f) << 10) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F11(x) (((x) & 0x1f) << 5) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F10(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL7_ICOMP2_E BIT(24) -#define G1_REG_DEC_CTRL7_ISCALE2(x) (((x) & 0xff) << 16) -#define G1_REG_DEC_CTRL7_ISHIFT2(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) -#define G1_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) -#define G1_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) -#define G1_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) -#define G1_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) -#define G1_REG_ADDR_STR 0x030 -#define G1_REG_ADDR_DST 0x034 -#define G1_REG_ADDR_REF(i) (0x038 + ((i) * 0x4)) -#define G1_REG_ADDR_REF_FIELD_E BIT(1) -#define G1_REG_ADDR_REF_TOPC_E BIT(0) -#define G1_REG_REF_PIC(i) (0x078 + ((i) * 0x4)) -#define G1_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define G1_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define G1_REG_REF_PIC_MB_ADJ_0(x) (((x) & 0x7f) << 21) -#define G1_REG_REF_PIC_MB_ADJ_1(x) (((x) & 0x7f) << 14) -#define G1_REG_REF_PIC_MB_ADJ_2(x) (((x) & 0x7f) << 7) -#define G1_REG_REF_PIC_MB_ADJ_3(x) (((x) & 0x7f) << 0) -#define G1_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) -#define G1_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) -#define G1_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) -#define G1_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) -#define G1_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) -#define G1_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) -#define G1_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) -#define G1_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) -#define G1_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) -#define G1_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) -#define G1_REG_LT_REF 0x098 -#define G1_REG_VALID_REF 0x09c -#define G1_REG_ADDR_QTABLE 0x0a0 -#define G1_REG_ADDR_DIR_MV 0x0a4 -#define G1_REG_BD_REF_PIC(i) (0x0a8 + ((i) * 0x4)) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 25) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 20) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 15) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 10) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 5) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define G1_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) -#define G1_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) -#define G1_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) -#define G1_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) -#define G1_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) -#define G1_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) -#define G1_REG_BD_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) -#define G1_REG_BD_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) -#define G1_REG_BD_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) -#define G1_REG_BD_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) -#define G1_REG_BD_P_REF_PIC 0x0bc -#define G1_REG_BD_P_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 25) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 20) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 15) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 10) -#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 5) -#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 0) -#define G1_REG_ERR_CONC 0x0c0 -#define G1_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 23) -#define G1_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 15) -#define G1_REG_PRED_FLT 0x0c4 -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) -#define G1_REG_REF_BUF_CTRL 0x0cc -#define G1_REG_REF_BUF_CTRL_REFBU_E BIT(31) -#define G1_REG_REF_BUF_CTRL_REFBU_THR(x) (((x) & 0xfff) << 19) -#define G1_REG_REF_BUF_CTRL_REFBU_PICID(x) (((x) & 0x1f) << 14) -#define G1_REG_REF_BUF_CTRL_REFBU_EVAL_E BIT(13) -#define G1_REG_REF_BUF_CTRL_REFBU_FPARMOD_E BIT(12) -#define G1_REG_REF_BUF_CTRL_REFBU_Y_OFFSET(x) (((x) & 0x1ff) << 0) -#define G1_REG_REF_BUF_CTRL2 0x0dc -#define G1_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(31) -#define G1_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 19) -#define G1_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 14) -#define G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 0) -#define G1_REG_SOFT_RESET 0x194 - -/* Post-processor registers. */ -#define G1_REG_PP_INTERRUPT G1_SWREG(60) -#define G1_REG_PP_READY_IRQ BIT(12) -#define G1_REG_PP_IRQ BIT(8) -#define G1_REG_PP_IRQ_DIS BIT(4) -#define G1_REG_PP_PIPELINE_EN BIT(1) -#define G1_REG_PP_EXTERNAL_TRIGGER BIT(0) -#define G1_REG_PP_DEV_CONFIG G1_SWREG(61) -#define G1_REG_PP_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define G1_REG_PP_AXI_WR_ID(v) (((v) << 16) & GENMASK(23, 16)) -#define G1_REG_PP_INSWAP32_E(v) ((v) ? BIT(10) : 0) -#define G1_REG_PP_DATA_DISC_E(v) ((v) ? BIT(9) : 0) -#define G1_REG_PP_CLK_GATE_E(v) ((v) ? BIT(8) : 0) -#define G1_REG_PP_IN_ENDIAN(v) ((v) ? BIT(7) : 0) -#define G1_REG_PP_OUT_ENDIAN(v) ((v) ? BIT(6) : 0) -#define G1_REG_PP_OUTSWAP32_E(v) ((v) ? BIT(5) : 0) -#define G1_REG_PP_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) -#define G1_REG_PP_IN_LUMA_BASE G1_SWREG(63) -#define G1_REG_PP_IN_CB_BASE G1_SWREG(64) -#define G1_REG_PP_IN_CR_BASE G1_SWREG(65) -#define G1_REG_PP_OUT_LUMA_BASE G1_SWREG(66) -#define G1_REG_PP_OUT_CHROMA_BASE G1_SWREG(67) -#define G1_REG_PP_CONTRAST_ADJUST G1_SWREG(68) -#define G1_REG_PP_COLOR_CONVERSION G1_SWREG(69) -#define G1_REG_PP_COLOR_CONVERSION0 G1_SWREG(70) -#define G1_REG_PP_COLOR_CONVERSION1 G1_SWREG(71) -#define G1_REG_PP_INPUT_SIZE G1_SWREG(72) -#define G1_REG_PP_INPUT_SIZE_HEIGHT(v) (((v) << 9) & GENMASK(16, 9)) -#define G1_REG_PP_INPUT_SIZE_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) -#define G1_REG_PP_SCALING0 G1_SWREG(79) -#define G1_REG_PP_PADD_R(v) (((v) << 23) & GENMASK(27, 23)) -#define G1_REG_PP_PADD_G(v) (((v) << 18) & GENMASK(22, 18)) -#define G1_REG_PP_RANGEMAP_Y(v) ((v) ? BIT(31) : 0) -#define G1_REG_PP_RANGEMAP_C(v) ((v) ? BIT(30) : 0) -#define G1_REG_PP_YCBCR_RANGE(v) ((v) ? BIT(29) : 0) -#define G1_REG_PP_RGB_16(v) ((v) ? BIT(28) : 0) -#define G1_REG_PP_SCALING1 G1_SWREG(80) -#define G1_REG_PP_PADD_B(v) (((v) << 18) & GENMASK(22, 18)) -#define G1_REG_PP_MASK_R G1_SWREG(82) -#define G1_REG_PP_MASK_G G1_SWREG(83) -#define G1_REG_PP_MASK_B G1_SWREG(84) -#define G1_REG_PP_CONTROL G1_SWREG(85) -#define G1_REG_PP_CONTROL_IN_FMT(v) (((v) << 29) & GENMASK(31, 29)) -#define G1_REG_PP_CONTROL_OUT_FMT(v) (((v) << 26) & GENMASK(28, 26)) -#define G1_REG_PP_CONTROL_OUT_HEIGHT(v) (((v) << 15) & GENMASK(25, 15)) -#define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4)) -#define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88) -#define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92) -#define G1_REG_PP_FUSE G1_SWREG(99) - -#endif /* HANTRO_G1_REGS_H_ */ diff --git a/drivers/staging/media/hantro/hantro_g1_vp8_dec.c b/drivers/staging/media/hantro/hantro_g1_vp8_dec.c deleted file mode 100644 index 851eb67f19f5..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_vp8_dec.c +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP8 codec driver - * - * Copyright (C) 2019 Rockchip Electronics Co., Ltd. - * ZhiChao Yu - * - * Copyright (C) 2019 Google, Inc. - * Tomasz Figa - */ - -#include - -#include "hantro_hw.h" -#include "hantro.h" -#include "hantro_g1_regs.h" - -/* DCT partition base address regs */ -static const struct hantro_reg vp8_dec_dct_base[8] = { - { G1_REG_ADDR_STR, 0, 0xffffffff }, - { G1_REG_ADDR_REF(8), 0, 0xffffffff }, - { G1_REG_ADDR_REF(9), 0, 0xffffffff }, - { G1_REG_ADDR_REF(10), 0, 0xffffffff }, - { G1_REG_ADDR_REF(11), 0, 0xffffffff }, - { G1_REG_ADDR_REF(12), 0, 0xffffffff }, - { G1_REG_ADDR_REF(14), 0, 0xffffffff }, - { G1_REG_ADDR_REF(15), 0, 0xffffffff }, -}; - -/* Loop filter level regs */ -static const struct hantro_reg vp8_dec_lf_level[4] = { - { G1_REG_REF_PIC(2), 18, 0x3f }, - { G1_REG_REF_PIC(2), 12, 0x3f }, - { G1_REG_REF_PIC(2), 6, 0x3f }, - { G1_REG_REF_PIC(2), 0, 0x3f }, -}; - -/* Macroblock loop filter level adjustment regs */ -static const struct hantro_reg vp8_dec_mb_adj[4] = { - { G1_REG_REF_PIC(0), 21, 0x7f }, - { G1_REG_REF_PIC(0), 14, 0x7f }, - { G1_REG_REF_PIC(0), 7, 0x7f }, - { G1_REG_REF_PIC(0), 0, 0x7f }, -}; - -/* Reference frame adjustment regs */ -static const struct hantro_reg vp8_dec_ref_adj[4] = { - { G1_REG_REF_PIC(1), 21, 0x7f }, - { G1_REG_REF_PIC(1), 14, 0x7f }, - { G1_REG_REF_PIC(1), 7, 0x7f }, - { G1_REG_REF_PIC(1), 0, 0x7f }, -}; - -/* Quantizer */ -static const struct hantro_reg vp8_dec_quant[4] = { - { G1_REG_REF_PIC(3), 11, 0x7ff }, - { G1_REG_REF_PIC(3), 0, 0x7ff }, - { G1_REG_BD_REF_PIC(4), 11, 0x7ff }, - { G1_REG_BD_REF_PIC(4), 0, 0x7ff }, -}; - -/* Quantizer delta regs */ -static const struct hantro_reg vp8_dec_quant_delta[5] = { - { G1_REG_REF_PIC(3), 27, 0x1f }, - { G1_REG_REF_PIC(3), 22, 0x1f }, - { G1_REG_BD_REF_PIC(4), 27, 0x1f }, - { G1_REG_BD_REF_PIC(4), 22, 0x1f }, - { G1_REG_BD_P_REF_PIC, 27, 0x1f }, -}; - -/* DCT partition start bits regs */ -static const struct hantro_reg vp8_dec_dct_start_bits[8] = { - { G1_REG_DEC_CTRL2, 26, 0x3f }, { G1_REG_DEC_CTRL4, 26, 0x3f }, - { G1_REG_DEC_CTRL4, 20, 0x3f }, { G1_REG_DEC_CTRL7, 24, 0x3f }, - { G1_REG_DEC_CTRL7, 18, 0x3f }, { G1_REG_DEC_CTRL7, 12, 0x3f }, - { G1_REG_DEC_CTRL7, 6, 0x3f }, { G1_REG_DEC_CTRL7, 0, 0x3f }, -}; - -/* Precision filter tap regs */ -static const struct hantro_reg vp8_dec_pred_bc_tap[8][4] = { - { - { G1_REG_PRED_FLT, 22, 0x3ff }, - { G1_REG_PRED_FLT, 12, 0x3ff }, - { G1_REG_PRED_FLT, 2, 0x3ff }, - { G1_REG_REF_PIC(4), 22, 0x3ff }, - }, - { - { G1_REG_REF_PIC(4), 12, 0x3ff }, - { G1_REG_REF_PIC(4), 2, 0x3ff }, - { G1_REG_REF_PIC(5), 22, 0x3ff }, - { G1_REG_REF_PIC(5), 12, 0x3ff }, - }, - { - { G1_REG_REF_PIC(5), 2, 0x3ff }, - { G1_REG_REF_PIC(6), 22, 0x3ff }, - { G1_REG_REF_PIC(6), 12, 0x3ff }, - { G1_REG_REF_PIC(6), 2, 0x3ff }, - }, - { - { G1_REG_REF_PIC(7), 22, 0x3ff }, - { G1_REG_REF_PIC(7), 12, 0x3ff }, - { G1_REG_REF_PIC(7), 2, 0x3ff }, - { G1_REG_LT_REF, 22, 0x3ff }, - }, - { - { G1_REG_LT_REF, 12, 0x3ff }, - { G1_REG_LT_REF, 2, 0x3ff }, - { G1_REG_VALID_REF, 22, 0x3ff }, - { G1_REG_VALID_REF, 12, 0x3ff }, - }, - { - { G1_REG_VALID_REF, 2, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 2, 0x3ff }, - }, - { - { G1_REG_BD_REF_PIC(1), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(1), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(1), 2, 0x3ff }, - { G1_REG_BD_REF_PIC(2), 22, 0x3ff }, - }, - { - { G1_REG_BD_REF_PIC(2), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(2), 2, 0x3ff }, - { G1_REG_BD_REF_PIC(3), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(3), 12, 0x3ff }, - }, -}; - -/* - * Set loop filters - */ -static void cfg_lf(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - const struct v4l2_vp8_loop_filter *lf = &hdr->lf; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - u32 reg; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 lf_level = clamp(lf->level + seg->lf_update[i], - 0, 63); - - hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_lf_level[i], - seg->lf_update[i]); - } - - reg = G1_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); - if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) - reg |= G1_REG_REF_PIC_FILT_TYPE_E; - vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(0)); - - if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { - for (i = 0; i < 4; i++) { - hantro_reg_write(vpu, &vp8_dec_mb_adj[i], - lf->mb_mode_delta[i]); - hantro_reg_write(vpu, &vp8_dec_ref_adj[i], - lf->ref_frm_delta[i]); - } - } -} - -/* - * Set quantization parameters - */ -static void cfg_qp(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_quantization *q = &hdr->quant; - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], - 0, 127); - - hantro_reg_write(vpu, &vp8_dec_quant[i], quant); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_quant[i], - seg->quant_update[i]); - } - - hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); -} - -/* - * set control partition and DCT partition regs - * - * VP8 frame stream data layout: - * - * first_part_size parttion_sizes[0] - * ^ ^ - * src_dma | | - * ^ +--------+------+ +-----+-----+ - * | | control part | | | - * +--------+----------------+------------------+-----------+-----+-----------+ - * | tag 3B | extra 7B | hdr | mb_data | DCT sz | DCT part0 | ... | DCT partn | - * +--------+-----------------------------------+-----------+-----+-----------+ - * | | | | - * v +----+---+ v - * mb_start | src_dma_end - * v - * DCT size part - * (num_dct-1)*3B - * Note: - * 1. only key-frames have extra 7-bytes - * 2. all offsets are base on src_dma - * 3. number of DCT parts is 1, 2, 4 or 8 - * 4. the addresses set to the VPU must be 64-bits aligned - */ -static void cfg_parts(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_src; - u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; - u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; - u32 dct_size_part_size, dct_part_offset; - struct hantro_reg reg; - dma_addr_t src_dma; - u32 dct_part_total_len = 0; - u32 count = 0; - unsigned int i; - - vb2_src = hantro_get_src_buf(ctx); - src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - /* - * Calculate control partition mb data info - * @first_part_header_bits: bits offset of mb data from first - * part start pos - * @mb_offset_bits: bits offset of mb data from src_dma - * base addr - * @mb_offset_byte: bytes offset of mb data from src_dma - * base addr - * @mb_start_bits: bits offset of mb data from mb data - * 64bits alignment addr - */ - mb_offset_bits = first_part_offset * 8 + - hdr->first_part_header_bits + 8; - mb_offset_bytes = mb_offset_bits / 8; - mb_start_bits = mb_offset_bits - - (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; - mb_size = hdr->first_part_size - - (mb_offset_bytes - first_part_offset) + - (mb_offset_bytes & DEC_8190_ALIGN_MASK); - - /* Macroblock data aligned base addr */ - vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) - + src_dma, G1_REG_ADDR_REF(13)); - - /* Macroblock data start bits */ - reg.base = G1_REG_DEC_CTRL2; - reg.mask = 0x3f; - reg.shift = 18; - hantro_reg_write(vpu, ®, mb_start_bits); - - /* Macroblock aligned data length */ - reg.base = G1_REG_DEC_CTRL6; - reg.mask = 0x3fffff; - reg.shift = 0; - hantro_reg_write(vpu, ®, mb_size + 1); - - /* - * Calculate DCT partition info - * @dct_size_part_size: Containing sizes of DCT part, every DCT part - * has 3 bytes to store its size, except the last - * DCT part - * @dct_part_offset: bytes offset of DCT parts from src_dma base addr - * @dct_part_total_len: total size of all DCT parts - */ - dct_size_part_size = (hdr->num_dct_parts - 1) * 3; - dct_part_offset = first_part_offset + hdr->first_part_size; - for (i = 0; i < hdr->num_dct_parts; i++) - dct_part_total_len += hdr->dct_part_sizes[i]; - dct_part_total_len += dct_size_part_size; - dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); - - /* Number of DCT partitions */ - reg.base = G1_REG_DEC_CTRL6; - reg.mask = 0xf; - reg.shift = 24; - hantro_reg_write(vpu, ®, hdr->num_dct_parts - 1); - - /* DCT partition length */ - vdpu_write_relaxed(vpu, - G1_REG_DEC_CTRL3_STREAM_LEN(dct_part_total_len), - G1_REG_DEC_CTRL3); - - /* DCT partitions base address */ - for (i = 0; i < hdr->num_dct_parts; i++) { - u32 byte_offset = dct_part_offset + dct_size_part_size + count; - u32 base_addr = byte_offset + src_dma; - - hantro_reg_write(vpu, &vp8_dec_dct_base[i], - base_addr & (~DEC_8190_ALIGN_MASK)); - - hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], - (byte_offset & DEC_8190_ALIGN_MASK) * 8); - - count += hdr->dct_part_sizes[i]; - } -} - -/* - * prediction filter taps - * normal 6-tap filters - */ -static void cfg_tap(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_reg reg; - u32 val = 0; - int i, j; - - reg.base = G1_REG_BD_REF_PIC(3); - reg.mask = 0xf; - - if ((hdr->version & 0x03) != 0) - return; /* Tap filter not used. */ - - for (i = 0; i < 8; i++) { - val = (hantro_vp8_dec_mc_filter[i][0] << 2) | - hantro_vp8_dec_mc_filter[i][5]; - - for (j = 0; j < 4; j++) - hantro_reg_write(vpu, &vp8_dec_pred_bc_tap[i][j], - hantro_vp8_dec_mc_filter[i][j + 1]); - - switch (i) { - case 2: - reg.shift = 8; - break; - case 4: - reg.shift = 4; - break; - case 6: - reg.shift = 0; - break; - default: - continue; - } - - hantro_reg_write(vpu, ®, val); - } -} - -static void cfg_ref(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_dev *vpu = ctx->dev; - dma_addr_t ref; - - - ref = hantro_get_ref(ctx, hdr->last_frame_ts); - if (!ref) { - vpu_debug(0, "failed to find last frame ts=%llu\n", - hdr->last_frame_ts); - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - } - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(0)); - - ref = hantro_get_ref(ctx, hdr->golden_frame_ts); - if (!ref && hdr->golden_frame_ts) - vpu_debug(0, "failed to find golden frame ts=%llu\n", - hdr->golden_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) - ref |= G1_REG_ADDR_REF_TOPC_E; - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(4)); - - ref = hantro_get_ref(ctx, hdr->alt_frame_ts); - if (!ref && hdr->alt_frame_ts) - vpu_debug(0, "failed to find alt frame ts=%llu\n", - hdr->alt_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) - ref |= G1_REG_ADDR_REF_TOPC_E; - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(5)); -} - -static void cfg_buffers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t dst_dma; - u32 reg; - - /* Set probability table buffer address */ - vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, - G1_REG_ADDR_QTABLE); - - /* Set segment map address */ - reg = G1_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { - reg |= G1_REG_FWD_PIC1_SEGMENT_E; - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) - reg |= G1_REG_FWD_PIC1_SEGMENT_UPD_E; - } - vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(0)); - - dst_dma = hantro_get_dec_buf_addr(ctx, &vb2_dst->vb2_buf); - vdpu_write_relaxed(vpu, dst_dma, G1_REG_ADDR_DST); -} - -int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp8_frame *hdr; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - size_t height = ctx->dst_fmt.height; - size_t width = ctx->dst_fmt.width; - u32 mb_width, mb_height; - u32 reg; - - hantro_start_prepare_run(ctx); - - hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); - if (WARN_ON(!hdr)) - return -EINVAL; - - /* Reset segment_map buffer in keyframe */ - if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) - memset(ctx->vp8_dec.segment_map.cpu, 0, - ctx->vp8_dec.segment_map.size); - - hantro_vp8_prob_update(ctx, hdr); - - reg = G1_REG_CONFIG_DEC_TIMEOUT_E | - G1_REG_CONFIG_DEC_STRENDIAN_E | - G1_REG_CONFIG_DEC_INSWAP32_E | - G1_REG_CONFIG_DEC_STRSWAP32_E | - G1_REG_CONFIG_DEC_OUTSWAP32_E | - G1_REG_CONFIG_DEC_CLK_GATE_E | - G1_REG_CONFIG_DEC_IN_ENDIAN | - G1_REG_CONFIG_DEC_OUT_ENDIAN | - G1_REG_CONFIG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, G1_REG_CONFIG); - - reg = G1_REG_DEC_CTRL0_DEC_MODE(10) | - G1_REG_DEC_CTRL0_DEC_AXI_AUTO; - if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) - reg |= G1_REG_DEC_CTRL0_PIC_INTER_E; - if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) - reg |= G1_REG_DEC_CTRL0_SKIP_MODE; - if (hdr->lf.level == 0) - reg |= G1_REG_DEC_CTRL0_FILTERING_DIS; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); - - /* Frame dimensions */ - mb_width = MB_WIDTH(width); - mb_height = MB_HEIGHT(height); - reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(mb_width) | - G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(mb_height) | - G1_REG_DEC_CTRL1_PIC_MB_W_EXT(mb_width >> 9) | - G1_REG_DEC_CTRL1_PIC_MB_H_EXT(mb_height >> 8); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); - - /* Boolean decoder */ - reg = G1_REG_DEC_CTRL2_BOOLEAN_RANGE(hdr->coder_state.range) - | G1_REG_DEC_CTRL2_BOOLEAN_VALUE(hdr->coder_state.value); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); - - reg = 0; - if (hdr->version != 3) - reg |= G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT; - if (hdr->version & 0x3) - reg |= G1_REG_DEC_CTRL4_BILIN_MC_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); - - cfg_lf(ctx, hdr); - cfg_qp(ctx, hdr); - cfg_parts(ctx, hdr); - cfg_tap(ctx, hdr); - - vb2_dst = hantro_get_dst_buf(ctx); - cfg_ref(ctx, hdr, vb2_dst); - cfg_buffers(ctx, hdr, vb2_dst); - - hantro_end_prepare_run(ctx); - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g2.c b/drivers/staging/media/hantro/hantro_g2.c deleted file mode 100644 index ee5f14c5f8f2..000000000000 --- a/drivers/staging/media/hantro/hantro_g2.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2021 Collabora Ltd, Andrzej Pietrasiewicz - */ - -#include "hantro_hw.h" -#include "hantro_g2_regs.h" - -void hantro_g2_check_idle(struct hantro_dev *vpu) -{ - int i; - - for (i = 0; i < 3; i++) { - u32 status; - - /* Make sure the VPU is idle */ - status = vdpu_read(vpu, G2_REG_INTERRUPT); - if (status & G2_REG_INTERRUPT_DEC_E) { - dev_warn(vpu->dev, "device still running, aborting"); - status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS; - vdpu_write(vpu, status, G2_REG_INTERRUPT); - } - } -} - -irqreturn_t hantro_g2_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, G2_REG_INTERRUPT); - state = (status & G2_REG_INTERRUPT_DEC_RDY_INT) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, G2_REG_INTERRUPT); - vdpu_write(vpu, G2_REG_CONFIG_DEC_CLK_GATE_E, G2_REG_CONFIG); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c deleted file mode 100644 index 233ecd863d5f..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c +++ /dev/null @@ -1,629 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU HEVC codec driver - * - * Copyright (C) 2020 Safran Passenger Innovations LLC - */ - -#include "hantro_hw.h" -#include "hantro_g2_regs.h" - -#define G2_ALIGN 16 - -static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx) -{ - return ctx->dst_fmt.width * ctx->dst_fmt.height; -} - -static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx) -{ - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - -static void prepare_tile_info_buffer(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu); - unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1; - unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; - unsigned int pic_width_in_ctbs, pic_height_in_ctbs; - unsigned int max_log2_ctb_size, ctb_size; - bool tiles_enabled, uniform_spacing; - u32 no_chroma = 0; - - tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); - uniform_spacing = !!(pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING); - - hantro_reg_write(vpu, &g2_tile_e, tiles_enabled); - - max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 + - sps->log2_diff_max_min_luma_coding_block_size; - pic_width_in_ctbs = (sps->pic_width_in_luma_samples + - (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size; - pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1) - >> max_log2_ctb_size; - ctb_size = 1 << max_log2_ctb_size; - - vpu_debug(1, "Preparing tile sizes buffer for %dx%d CTBs (CTB size %d)\n", - pic_width_in_ctbs, pic_height_in_ctbs, ctb_size); - - if (tiles_enabled) { - unsigned int i, j, h; - - vpu_debug(1, "Tiles enabled! %dx%d\n", num_tile_cols, num_tile_rows); - - hantro_reg_write(vpu, &g2_num_tile_rows, num_tile_rows); - hantro_reg_write(vpu, &g2_num_tile_cols, num_tile_cols); - - /* write width + height for each tile in pic */ - if (!uniform_spacing) { - u32 tmp_w = 0, tmp_h = 0; - - for (i = 0; i < num_tile_rows; i++) { - if (i == num_tile_rows - 1) - h = pic_height_in_ctbs - tmp_h; - else - h = pps->row_height_minus1[i] + 1; - tmp_h += h; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - for (j = 0, tmp_w = 0; j < num_tile_cols - 1; j++) { - tmp_w += pps->column_width_minus1[j] + 1; - *p++ = pps->column_width_minus1[j] + 1; - *p++ = h; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - } - /* last column */ - *p++ = pic_width_in_ctbs - tmp_w; - *p++ = h; - } - } else { /* uniform spacing */ - u32 tmp, prev_h, prev_w; - - for (i = 0, prev_h = 0; i < num_tile_rows; i++) { - tmp = (i + 1) * pic_height_in_ctbs / num_tile_rows; - h = tmp - prev_h; - prev_h = tmp; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - for (j = 0, prev_w = 0; j < num_tile_cols; j++) { - tmp = (j + 1) * pic_width_in_ctbs / num_tile_cols; - *p++ = tmp - prev_w; - *p++ = h; - if (j == 0 && - (pps->column_width_minus1[0] + 1) == 1 && - ctb_size == 16) - no_chroma = 1; - prev_w = tmp; - } - } - } - } else { - hantro_reg_write(vpu, &g2_num_tile_rows, 1); - hantro_reg_write(vpu, &g2_num_tile_cols, 1); - - /* There's one tile, with dimensions equal to pic size. */ - p[0] = pic_width_in_ctbs; - p[1] = pic_height_in_ctbs; - } - - if (no_chroma) - vpu_debug(1, "%s: no chroma!\n", __func__); -} - -static int compute_header_skip_length(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - int skip = 0; - - if (pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT) - /* size of pic_output_flag */ - skip++; - - if (sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) - /* size of pic_order_cnt_lsb */ - skip += 2; - - if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) { - /* size of pic_order_cnt_lsb */ - skip += sps->log2_max_pic_order_cnt_lsb_minus4 + 4; - - /* size of short_term_ref_pic_set_sps_flag */ - skip++; - - if (decode_params->short_term_ref_pic_set_size) - /* size of st_ref_pic_set( num_short_term_ref_pic_sets ) */ - skip += decode_params->short_term_ref_pic_set_size; - else if (sps->num_short_term_ref_pic_sets > 1) - skip += fls(sps->num_short_term_ref_pic_sets - 1); - - skip += decode_params->long_term_ref_pic_set_size; - } - - return skip; -} - -static void set_params(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - struct hantro_dev *vpu = ctx->dev; - u32 min_log2_cb_size, max_log2_ctb_size, min_cb_size, max_ctb_size; - u32 pic_width_in_min_cbs, pic_height_in_min_cbs; - u32 pic_width_aligned, pic_height_aligned; - u32 partial_ctb_x, partial_ctb_y; - - hantro_reg_write(vpu, &g2_bit_depth_y_minus8, sps->bit_depth_luma_minus8); - hantro_reg_write(vpu, &g2_bit_depth_c_minus8, sps->bit_depth_chroma_minus8); - - hantro_reg_write(vpu, &g2_output_8_bits, 0); - - hantro_reg_write(vpu, &g2_hdr_skip_length, compute_header_skip_length(ctx)); - - min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; - max_log2_ctb_size = min_log2_cb_size + sps->log2_diff_max_min_luma_coding_block_size; - - hantro_reg_write(vpu, &g2_min_cb_size, min_log2_cb_size); - hantro_reg_write(vpu, &g2_max_cb_size, max_log2_ctb_size); - - min_cb_size = 1 << min_log2_cb_size; - max_ctb_size = 1 << max_log2_ctb_size; - - pic_width_in_min_cbs = sps->pic_width_in_luma_samples / min_cb_size; - pic_height_in_min_cbs = sps->pic_height_in_luma_samples / min_cb_size; - pic_width_aligned = ALIGN(sps->pic_width_in_luma_samples, max_ctb_size); - pic_height_aligned = ALIGN(sps->pic_height_in_luma_samples, max_ctb_size); - - partial_ctb_x = !!(sps->pic_width_in_luma_samples != pic_width_aligned); - partial_ctb_y = !!(sps->pic_height_in_luma_samples != pic_height_aligned); - - hantro_reg_write(vpu, &g2_partial_ctb_x, partial_ctb_x); - hantro_reg_write(vpu, &g2_partial_ctb_y, partial_ctb_y); - - hantro_reg_write(vpu, &g2_pic_width_in_cbs, pic_width_in_min_cbs); - hantro_reg_write(vpu, &g2_pic_height_in_cbs, pic_height_in_min_cbs); - - hantro_reg_write(vpu, &g2_pic_width_4x4, - (pic_width_in_min_cbs * min_cb_size) / 4); - hantro_reg_write(vpu, &g2_pic_height_4x4, - (pic_height_in_min_cbs * min_cb_size) / 4); - - hantro_reg_write(vpu, &hevc_max_inter_hierdepth, - sps->max_transform_hierarchy_depth_inter); - hantro_reg_write(vpu, &hevc_max_intra_hierdepth, - sps->max_transform_hierarchy_depth_intra); - hantro_reg_write(vpu, &hevc_min_trb_size, - sps->log2_min_luma_transform_block_size_minus2 + 2); - hantro_reg_write(vpu, &hevc_max_trb_size, - sps->log2_min_luma_transform_block_size_minus2 + 2 + - sps->log2_diff_max_min_luma_transform_block_size); - - hantro_reg_write(vpu, &g2_tempor_mvp_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) && - !(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)); - hantro_reg_write(vpu, &g2_strong_smooth_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); - hantro_reg_write(vpu, &g2_asym_pred_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); - hantro_reg_write(vpu, &g2_sao_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); - hantro_reg_write(vpu, &g2_sign_data_hide, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); - - if (pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED) { - hantro_reg_write(vpu, &g2_cu_qpd_e, 1); - hantro_reg_write(vpu, &g2_max_cu_qpd_depth, pps->diff_cu_qp_delta_depth); - } else { - hantro_reg_write(vpu, &g2_cu_qpd_e, 0); - hantro_reg_write(vpu, &g2_max_cu_qpd_depth, 0); - } - - hantro_reg_write(vpu, &g2_cb_qp_offset, pps->pps_cb_qp_offset); - hantro_reg_write(vpu, &g2_cr_qp_offset, pps->pps_cr_qp_offset); - - hantro_reg_write(vpu, &g2_filt_offset_beta, pps->pps_beta_offset_div2); - hantro_reg_write(vpu, &g2_filt_offset_tc, pps->pps_tc_offset_div2); - hantro_reg_write(vpu, &g2_slice_hdr_ext_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); - hantro_reg_write(vpu, &g2_slice_hdr_ext_bits, pps->num_extra_slice_header_bits); - hantro_reg_write(vpu, &g2_slice_chqp_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); - hantro_reg_write(vpu, &g2_weight_bipr_idc, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); - hantro_reg_write(vpu, &g2_transq_bypass, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); - hantro_reg_write(vpu, &g2_list_mod_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); - hantro_reg_write(vpu, &g2_entropy_sync_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); - hantro_reg_write(vpu, &g2_cabac_init_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); - hantro_reg_write(vpu, &g2_idr_pic_e, - !!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC)); - hantro_reg_write(vpu, &hevc_parallel_merge, - pps->log2_parallel_merge_level_minus2 + 2); - hantro_reg_write(vpu, &g2_pcm_filt_d, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); - hantro_reg_write(vpu, &g2_pcm_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)); - if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) { - hantro_reg_write(vpu, &g2_max_pcm_size, - sps->log2_diff_max_min_pcm_luma_coding_block_size + - sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); - hantro_reg_write(vpu, &g2_min_pcm_size, - sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); - hantro_reg_write(vpu, &g2_bit_depth_pcm_y, - sps->pcm_sample_bit_depth_luma_minus1 + 1); - hantro_reg_write(vpu, &g2_bit_depth_pcm_c, - sps->pcm_sample_bit_depth_chroma_minus1 + 1); - } else { - hantro_reg_write(vpu, &g2_max_pcm_size, 0); - hantro_reg_write(vpu, &g2_min_pcm_size, 0); - hantro_reg_write(vpu, &g2_bit_depth_pcm_y, 0); - hantro_reg_write(vpu, &g2_bit_depth_pcm_c, 0); - } - - hantro_reg_write(vpu, &g2_start_code_e, 1); - hantro_reg_write(vpu, &g2_init_qp, pps->init_qp_minus26 + 26); - hantro_reg_write(vpu, &g2_weight_pred_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); - hantro_reg_write(vpu, &g2_cabac_init_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); - hantro_reg_write(vpu, &g2_const_intra_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); - hantro_reg_write(vpu, &g2_transform_skip, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); - hantro_reg_write(vpu, &g2_out_filtering_dis, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); - hantro_reg_write(vpu, &g2_filt_ctrl_pres, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); - hantro_reg_write(vpu, &g2_dependent_slice, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); - hantro_reg_write(vpu, &g2_filter_override, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); - hantro_reg_write(vpu, &g2_refidx0_active, - pps->num_ref_idx_l0_default_active_minus1 + 1); - hantro_reg_write(vpu, &g2_refidx1_active, - pps->num_ref_idx_l1_default_active_minus1 + 1); - hantro_reg_write(vpu, &g2_apf_threshold, 8); -} - -static void set_ref_pic_list(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - struct hantro_dev *vpu = ctx->dev; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - u32 list0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; - u32 list1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; - static const struct hantro_reg ref_pic_regs0[] = { - hevc_rlist_f0, - hevc_rlist_f1, - hevc_rlist_f2, - hevc_rlist_f3, - hevc_rlist_f4, - hevc_rlist_f5, - hevc_rlist_f6, - hevc_rlist_f7, - hevc_rlist_f8, - hevc_rlist_f9, - hevc_rlist_f10, - hevc_rlist_f11, - hevc_rlist_f12, - hevc_rlist_f13, - hevc_rlist_f14, - hevc_rlist_f15, - }; - static const struct hantro_reg ref_pic_regs1[] = { - hevc_rlist_b0, - hevc_rlist_b1, - hevc_rlist_b2, - hevc_rlist_b3, - hevc_rlist_b4, - hevc_rlist_b5, - hevc_rlist_b6, - hevc_rlist_b7, - hevc_rlist_b8, - hevc_rlist_b9, - hevc_rlist_b10, - hevc_rlist_b11, - hevc_rlist_b12, - hevc_rlist_b13, - hevc_rlist_b14, - hevc_rlist_b15, - }; - unsigned int i, j; - - /* List 0 contains: short term before, short term after and long term */ - j = 0; - for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_st_curr_before[i]; - for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_st_curr_after[i]; - for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_lt_curr[i]; - - /* Fill the list, copying over and over */ - i = 0; - while (j < ARRAY_SIZE(list0)) - list0[j++] = list0[i++]; - - j = 0; - for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_st_curr_after[i]; - for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_st_curr_before[i]; - for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_lt_curr[i]; - - i = 0; - while (j < ARRAY_SIZE(list1)) - list1[j++] = list1[i++]; - - for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - hantro_reg_write(vpu, &ref_pic_regs0[i], list0[i]); - hantro_reg_write(vpu, &ref_pic_regs1[i], list1[i]); - } -} - -static int set_ref(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; - dma_addr_t luma_addr, chroma_addr, mv_addr = 0; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - struct hantro_decoded_buffer *dst; - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - size_t mv_offset = hantro_hevc_motion_vectors_offset(ctx); - u32 max_ref_frames; - u16 dpb_longterm_e; - static const struct hantro_reg cur_poc[] = { - hevc_cur_poc_00, - hevc_cur_poc_01, - hevc_cur_poc_02, - hevc_cur_poc_03, - hevc_cur_poc_04, - hevc_cur_poc_05, - hevc_cur_poc_06, - hevc_cur_poc_07, - hevc_cur_poc_08, - hevc_cur_poc_09, - hevc_cur_poc_10, - hevc_cur_poc_11, - hevc_cur_poc_12, - hevc_cur_poc_13, - hevc_cur_poc_14, - hevc_cur_poc_15, - }; - unsigned int i; - - max_ref_frames = decode_params->num_poc_lt_curr + - decode_params->num_poc_st_curr_before + - decode_params->num_poc_st_curr_after; - /* - * Set max_ref_frames to non-zero to avoid HW hang when decoding - * badly marked I-frames. - */ - max_ref_frames = max_ref_frames ? max_ref_frames : 1; - hantro_reg_write(vpu, &g2_num_ref_frames, max_ref_frames); - hantro_reg_write(vpu, &g2_filter_over_slices, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); - hantro_reg_write(vpu, &g2_filter_over_tiles, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); - - /* - * Write POC count diff from current pic. - */ - for (i = 0; i < decode_params->num_active_dpb_entries && i < ARRAY_SIZE(cur_poc); i++) { - char poc_diff = decode_params->pic_order_cnt_val - dpb[i].pic_order_cnt_val; - - hantro_reg_write(vpu, &cur_poc[i], poc_diff); - } - - if (i < ARRAY_SIZE(cur_poc)) { - /* - * After the references, fill one entry pointing to itself, - * i.e. difference is zero. - */ - hantro_reg_write(vpu, &cur_poc[i], 0); - i++; - } - - /* Fill the rest with the current picture */ - for (; i < ARRAY_SIZE(cur_poc); i++) - hantro_reg_write(vpu, &cur_poc[i], decode_params->pic_order_cnt_val); - - set_ref_pic_list(ctx); - - /* We will only keep the reference pictures that are still used */ - hantro_hevc_ref_init(ctx); - - /* Set up addresses of DPB buffers */ - dpb_longterm_e = 0; - for (i = 0; i < decode_params->num_active_dpb_entries && - i < (V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1); i++) { - luma_addr = hantro_hevc_get_ref_buf(ctx, dpb[i].pic_order_cnt_val); - if (!luma_addr) - return -ENOMEM; - - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; - - if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) - dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); - - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); - } - - vb2_dst = hantro_get_dst_buf(ctx); - dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); - luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - if (!luma_addr) - return -ENOMEM; - - if (hantro_hevc_add_ref_buf(ctx, decode_params->pic_order_cnt_val, luma_addr)) - return -EINVAL; - - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; - - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr); - - hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr); - hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr); - hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr); - - for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0); - } - - hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e); - - return 0; -} - -static void set_buffers(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma; - u32 src_len, src_buf_len; - - src_buf = hantro_get_src_buf(ctx); - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - src_buf_len = vb2_plane_size(&src_buf->vb2_buf, 0); - - hantro_write_addr(vpu, G2_STREAM_ADDR, src_dma); - hantro_reg_write(vpu, &g2_stream_len, src_len); - hantro_reg_write(vpu, &g2_strm_buffer_len, src_buf_len); - hantro_reg_write(vpu, &g2_strm_start_offset, 0); - hantro_reg_write(vpu, &g2_write_mvs_e, 1); - - hantro_write_addr(vpu, G2_TILE_SIZES_ADDR, ctx->hevc_dec.tile_sizes.dma); - hantro_write_addr(vpu, G2_TILE_FILTER_ADDR, ctx->hevc_dec.tile_filter.dma); - hantro_write_addr(vpu, G2_TILE_SAO_ADDR, ctx->hevc_dec.tile_sao.dma); - hantro_write_addr(vpu, G2_TILE_BSD_ADDR, ctx->hevc_dec.tile_bsd.dma); -} - -static void prepare_scaling_list_buffer(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_scaling_matrix *sc = ctrls->scaling; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - u8 *p = ((u8 *)ctx->hevc_dec.scaling_lists.cpu); - unsigned int scaling_list_enabled; - unsigned int i, j, k; - - scaling_list_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); - hantro_reg_write(vpu, &g2_scaling_list_e, scaling_list_enabled); - - if (!scaling_list_enabled) - return; - - for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_16x16); i++) - *p++ = sc->scaling_list_dc_coef_16x16[i]; - - for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_32x32); i++) - *p++ = sc->scaling_list_dc_coef_32x32[i]; - - /* 128-bit boundary */ - p += 8; - - /* write scaling lists column by column */ - - for (i = 0; i < 6; i++) - for (j = 0; j < 4; j++) - for (k = 0; k < 4; k++) - *p++ = sc->scaling_list_4x4[i][4 * k + j]; - - for (i = 0; i < 6; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_8x8[i][8 * k + j]; - - for (i = 0; i < 6; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_16x16[i][8 * k + j]; - - for (i = 0; i < 2; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_32x32[i][8 * k + j]; - - hantro_write_addr(vpu, G2_HEVC_SCALING_LIST_ADDR, ctx->hevc_dec.scaling_lists.dma); -} - -int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - int ret; - - hantro_g2_check_idle(vpu); - - /* Prepare HEVC decoder context. */ - ret = hantro_hevc_dec_prepare_run(ctx); - if (ret) - return ret; - - /* Configure hardware registers. */ - set_params(ctx); - - /* set reference pictures */ - ret = set_ref(ctx); - if (ret) - return ret; - - set_buffers(ctx); - prepare_tile_info_buffer(ctx); - - prepare_scaling_list_buffer(ctx); - - hantro_end_prepare_run(ctx); - - hantro_reg_write(vpu, &g2_mode, HEVC_DEC_MODE); - hantro_reg_write(vpu, &g2_clk_gate_e, 1); - - /* Don't disable output */ - hantro_reg_write(vpu, &g2_out_dis, 0); - - /* Don't compress buffers */ - hantro_reg_write(vpu, &g2_ref_compress_bypass, 1); - - /* Bus width and max burst */ - hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128); - hantro_reg_write(vpu, &g2_max_burst, 16); - - /* Swap */ - hantro_reg_write(vpu, &g2_strm_swap, 0xf); - hantro_reg_write(vpu, &g2_dirmv_swap, 0xf); - hantro_reg_write(vpu, &g2_compress_swap, 0xf); - - /* Start decoding! */ - vdpu_write(vpu, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g2_regs.h b/drivers/staging/media/hantro/hantro_g2_regs.h deleted file mode 100644 index 82606783591a..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_regs.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2021, Collabora - * - * Author: Benjamin Gaignard - */ - -#ifndef HANTRO_G2_REGS_H_ -#define HANTRO_G2_REGS_H_ - -#include "hantro.h" - -#define G2_SWREG(nr) ((nr) * 4) - -#define G2_DEC_REG(b, s, m) \ - ((const struct hantro_reg) { \ - .base = G2_SWREG(b), \ - .shift = s, \ - .mask = m, \ - }) - -#define G2_REG_VERSION G2_SWREG(0) - -#define G2_REG_INTERRUPT G2_SWREG(1) -#define G2_REG_INTERRUPT_DEC_RDY_INT BIT(12) -#define G2_REG_INTERRUPT_DEC_ABORT_E BIT(5) -#define G2_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) -#define G2_REG_INTERRUPT_DEC_E BIT(0) - -#define HEVC_DEC_MODE 0xc -#define VP9_DEC_MODE 0xd - -#define BUS_WIDTH_32 0 -#define BUS_WIDTH_64 1 -#define BUS_WIDTH_128 2 -#define BUS_WIDTH_256 3 - -#define g2_strm_swap G2_DEC_REG(2, 28, 0xf) -#define g2_strm_swap_old G2_DEC_REG(2, 27, 0x1f) -#define g2_pic_swap G2_DEC_REG(2, 22, 0x1f) -#define g2_dirmv_swap G2_DEC_REG(2, 20, 0xf) -#define g2_dirmv_swap_old G2_DEC_REG(2, 17, 0x1f) -#define g2_tab0_swap_old G2_DEC_REG(2, 12, 0x1f) -#define g2_tab1_swap_old G2_DEC_REG(2, 7, 0x1f) -#define g2_tab2_swap_old G2_DEC_REG(2, 2, 0x1f) - -#define g2_mode G2_DEC_REG(3, 27, 0x1f) -#define g2_compress_swap G2_DEC_REG(3, 20, 0xf) -#define g2_ref_compress_bypass G2_DEC_REG(3, 17, 0x1) -#define g2_out_rs_e G2_DEC_REG(3, 16, 0x1) -#define g2_out_dis G2_DEC_REG(3, 15, 0x1) -#define g2_out_filtering_dis G2_DEC_REG(3, 14, 0x1) -#define g2_write_mvs_e G2_DEC_REG(3, 12, 0x1) -#define g2_tab3_swap_old G2_DEC_REG(3, 7, 0x1f) -#define g2_rscan_swap G2_DEC_REG(3, 2, 0x1f) - -#define g2_pic_width_in_cbs G2_DEC_REG(4, 19, 0x1fff) -#define g2_pic_height_in_cbs G2_DEC_REG(4, 6, 0x1fff) -#define g2_num_ref_frames G2_DEC_REG(4, 0, 0x1f) - -#define g2_start_bit G2_DEC_REG(5, 25, 0x7f) -#define g2_scaling_list_e G2_DEC_REG(5, 24, 0x1) -#define g2_cb_qp_offset G2_DEC_REG(5, 19, 0x1f) -#define g2_cr_qp_offset G2_DEC_REG(5, 14, 0x1f) -#define g2_sign_data_hide G2_DEC_REG(5, 12, 0x1) -#define g2_tempor_mvp_e G2_DEC_REG(5, 11, 0x1) -#define g2_max_cu_qpd_depth G2_DEC_REG(5, 5, 0x3f) -#define g2_cu_qpd_e G2_DEC_REG(5, 4, 0x1) -#define g2_pix_shift G2_DEC_REG(5, 0, 0xf) - -#define g2_stream_len G2_DEC_REG(6, 0, 0xffffffff) - -#define g2_cabac_init_present G2_DEC_REG(7, 31, 0x1) -#define g2_weight_pred_e G2_DEC_REG(7, 28, 0x1) -#define g2_weight_bipr_idc G2_DEC_REG(7, 26, 0x3) -#define g2_filter_over_slices G2_DEC_REG(7, 25, 0x1) -#define g2_filter_over_tiles G2_DEC_REG(7, 24, 0x1) -#define g2_asym_pred_e G2_DEC_REG(7, 23, 0x1) -#define g2_sao_e G2_DEC_REG(7, 22, 0x1) -#define g2_pcm_filt_d G2_DEC_REG(7, 21, 0x1) -#define g2_slice_chqp_present G2_DEC_REG(7, 20, 0x1) -#define g2_dependent_slice G2_DEC_REG(7, 19, 0x1) -#define g2_filter_override G2_DEC_REG(7, 18, 0x1) -#define g2_strong_smooth_e G2_DEC_REG(7, 17, 0x1) -#define g2_filt_offset_beta G2_DEC_REG(7, 12, 0x1f) -#define g2_filt_offset_tc G2_DEC_REG(7, 7, 0x1f) -#define g2_slice_hdr_ext_e G2_DEC_REG(7, 6, 0x1) -#define g2_slice_hdr_ext_bits G2_DEC_REG(7, 3, 0x7) - -#define g2_const_intra_e G2_DEC_REG(8, 31, 0x1) -#define g2_filt_ctrl_pres G2_DEC_REG(8, 30, 0x1) -#define g2_bit_depth_y G2_DEC_REG(8, 21, 0xf) -#define g2_bit_depth_c G2_DEC_REG(8, 17, 0xf) -#define g2_idr_pic_e G2_DEC_REG(8, 16, 0x1) -#define g2_bit_depth_pcm_y G2_DEC_REG(8, 12, 0xf) -#define g2_bit_depth_pcm_c G2_DEC_REG(8, 8, 0xf) -#define g2_bit_depth_y_minus8 G2_DEC_REG(8, 6, 0x3) -#define g2_bit_depth_c_minus8 G2_DEC_REG(8, 4, 0x3) -#define g2_rs_out_bit_depth G2_DEC_REG(8, 4, 0xf) -#define g2_output_8_bits G2_DEC_REG(8, 3, 0x1) -#define g2_output_format G2_DEC_REG(8, 0, 0x7) -#define g2_pp_pix_shift G2_DEC_REG(8, 0, 0xf) - -#define g2_refidx1_active G2_DEC_REG(9, 19, 0x1f) -#define g2_refidx0_active G2_DEC_REG(9, 14, 0x1f) -#define g2_hdr_skip_length G2_DEC_REG(9, 0, 0x3fff) - -#define g2_start_code_e G2_DEC_REG(10, 31, 0x1) -#define g2_init_qp_old G2_DEC_REG(10, 25, 0x3f) -#define g2_init_qp G2_DEC_REG(10, 24, 0x7f) -#define g2_num_tile_cols_old G2_DEC_REG(10, 20, 0x1f) -#define g2_num_tile_cols G2_DEC_REG(10, 19, 0x1f) -#define g2_num_tile_rows_old G2_DEC_REG(10, 15, 0x1f) -#define g2_num_tile_rows G2_DEC_REG(10, 14, 0x1f) -#define g2_tile_e G2_DEC_REG(10, 1, 0x1) -#define g2_entropy_sync_e G2_DEC_REG(10, 0, 0x1) - -#define vp9_transform_mode G2_DEC_REG(11, 27, 0x7) -#define vp9_filt_sharpness G2_DEC_REG(11, 21, 0x7) -#define vp9_mcomp_filt_type G2_DEC_REG(11, 8, 0x7) -#define vp9_high_prec_mv_e G2_DEC_REG(11, 7, 0x1) -#define vp9_comp_pred_mode G2_DEC_REG(11, 4, 0x3) -#define vp9_gref_sign_bias G2_DEC_REG(11, 2, 0x1) -#define vp9_aref_sign_bias G2_DEC_REG(11, 0, 0x1) - -#define g2_refer_lterm_e G2_DEC_REG(12, 16, 0xffff) -#define g2_min_cb_size G2_DEC_REG(12, 13, 0x7) -#define g2_max_cb_size G2_DEC_REG(12, 10, 0x7) -#define g2_min_pcm_size G2_DEC_REG(12, 7, 0x7) -#define g2_max_pcm_size G2_DEC_REG(12, 4, 0x7) -#define g2_pcm_e G2_DEC_REG(12, 3, 0x1) -#define g2_transform_skip G2_DEC_REG(12, 2, 0x1) -#define g2_transq_bypass G2_DEC_REG(12, 1, 0x1) -#define g2_list_mod_e G2_DEC_REG(12, 0, 0x1) - -#define hevc_min_trb_size G2_DEC_REG(13, 13, 0x7) -#define hevc_max_trb_size G2_DEC_REG(13, 10, 0x7) -#define hevc_max_intra_hierdepth G2_DEC_REG(13, 7, 0x7) -#define hevc_max_inter_hierdepth G2_DEC_REG(13, 4, 0x7) -#define hevc_parallel_merge G2_DEC_REG(13, 0, 0xf) - -#define hevc_rlist_f0 G2_DEC_REG(14, 0, 0x1f) -#define hevc_rlist_f1 G2_DEC_REG(14, 10, 0x1f) -#define hevc_rlist_f2 G2_DEC_REG(14, 20, 0x1f) -#define hevc_rlist_b0 G2_DEC_REG(14, 5, 0x1f) -#define hevc_rlist_b1 G2_DEC_REG(14, 15, 0x1f) -#define hevc_rlist_b2 G2_DEC_REG(14, 25, 0x1f) - -#define hevc_rlist_f3 G2_DEC_REG(15, 0, 0x1f) -#define hevc_rlist_f4 G2_DEC_REG(15, 10, 0x1f) -#define hevc_rlist_f5 G2_DEC_REG(15, 20, 0x1f) -#define hevc_rlist_b3 G2_DEC_REG(15, 5, 0x1f) -#define hevc_rlist_b4 G2_DEC_REG(15, 15, 0x1f) -#define hevc_rlist_b5 G2_DEC_REG(15, 25, 0x1f) - -#define hevc_rlist_f6 G2_DEC_REG(16, 0, 0x1f) -#define hevc_rlist_f7 G2_DEC_REG(16, 10, 0x1f) -#define hevc_rlist_f8 G2_DEC_REG(16, 20, 0x1f) -#define hevc_rlist_b6 G2_DEC_REG(16, 5, 0x1f) -#define hevc_rlist_b7 G2_DEC_REG(16, 15, 0x1f) -#define hevc_rlist_b8 G2_DEC_REG(16, 25, 0x1f) - -#define hevc_rlist_f9 G2_DEC_REG(17, 0, 0x1f) -#define hevc_rlist_f10 G2_DEC_REG(17, 10, 0x1f) -#define hevc_rlist_f11 G2_DEC_REG(17, 20, 0x1f) -#define hevc_rlist_b9 G2_DEC_REG(17, 5, 0x1f) -#define hevc_rlist_b10 G2_DEC_REG(17, 15, 0x1f) -#define hevc_rlist_b11 G2_DEC_REG(17, 25, 0x1f) - -#define hevc_rlist_f12 G2_DEC_REG(18, 0, 0x1f) -#define hevc_rlist_f13 G2_DEC_REG(18, 10, 0x1f) -#define hevc_rlist_f14 G2_DEC_REG(18, 20, 0x1f) -#define hevc_rlist_b12 G2_DEC_REG(18, 5, 0x1f) -#define hevc_rlist_b13 G2_DEC_REG(18, 15, 0x1f) -#define hevc_rlist_b14 G2_DEC_REG(18, 25, 0x1f) - -#define hevc_rlist_f15 G2_DEC_REG(19, 0, 0x1f) -#define hevc_rlist_b15 G2_DEC_REG(19, 5, 0x1f) - -#define g2_partial_ctb_x G2_DEC_REG(20, 31, 0x1) -#define g2_partial_ctb_y G2_DEC_REG(20, 30, 0x1) -#define g2_pic_width_4x4 G2_DEC_REG(20, 16, 0xfff) -#define g2_pic_height_4x4 G2_DEC_REG(20, 0, 0xfff) - -#define vp9_qp_delta_y_dc G2_DEC_REG(13, 23, 0x3f) -#define vp9_qp_delta_ch_dc G2_DEC_REG(13, 17, 0x3f) -#define vp9_qp_delta_ch_ac G2_DEC_REG(13, 11, 0x3f) -#define vp9_last_sign_bias G2_DEC_REG(13, 10, 0x1) -#define vp9_lossless_e G2_DEC_REG(13, 9, 0x1) -#define vp9_comp_pred_var_ref1 G2_DEC_REG(13, 7, 0x3) -#define vp9_comp_pred_var_ref0 G2_DEC_REG(13, 5, 0x3) -#define vp9_comp_pred_fixed_ref G2_DEC_REG(13, 3, 0x3) -#define vp9_segment_temp_upd_e G2_DEC_REG(13, 2, 0x1) -#define vp9_segment_upd_e G2_DEC_REG(13, 1, 0x1) -#define vp9_segment_e G2_DEC_REG(13, 0, 0x1) - -#define vp9_filt_level G2_DEC_REG(14, 18, 0x3f) -#define vp9_refpic_seg0 G2_DEC_REG(14, 15, 0x7) -#define vp9_skip_seg0 G2_DEC_REG(14, 14, 0x1) -#define vp9_filt_level_seg0 G2_DEC_REG(14, 8, 0x3f) -#define vp9_quant_seg0 G2_DEC_REG(14, 0, 0xff) - -#define vp9_refpic_seg1 G2_DEC_REG(15, 15, 0x7) -#define vp9_skip_seg1 G2_DEC_REG(15, 14, 0x1) -#define vp9_filt_level_seg1 G2_DEC_REG(15, 8, 0x3f) -#define vp9_quant_seg1 G2_DEC_REG(15, 0, 0xff) - -#define vp9_refpic_seg2 G2_DEC_REG(16, 15, 0x7) -#define vp9_skip_seg2 G2_DEC_REG(16, 14, 0x1) -#define vp9_filt_level_seg2 G2_DEC_REG(16, 8, 0x3f) -#define vp9_quant_seg2 G2_DEC_REG(16, 0, 0xff) - -#define vp9_refpic_seg3 G2_DEC_REG(17, 15, 0x7) -#define vp9_skip_seg3 G2_DEC_REG(17, 14, 0x1) -#define vp9_filt_level_seg3 G2_DEC_REG(17, 8, 0x3f) -#define vp9_quant_seg3 G2_DEC_REG(17, 0, 0xff) - -#define vp9_refpic_seg4 G2_DEC_REG(18, 15, 0x7) -#define vp9_skip_seg4 G2_DEC_REG(18, 14, 0x1) -#define vp9_filt_level_seg4 G2_DEC_REG(18, 8, 0x3f) -#define vp9_quant_seg4 G2_DEC_REG(18, 0, 0xff) - -#define vp9_refpic_seg5 G2_DEC_REG(19, 15, 0x7) -#define vp9_skip_seg5 G2_DEC_REG(19, 14, 0x1) -#define vp9_filt_level_seg5 G2_DEC_REG(19, 8, 0x3f) -#define vp9_quant_seg5 G2_DEC_REG(19, 0, 0xff) - -#define hevc_cur_poc_00 G2_DEC_REG(46, 24, 0xff) -#define hevc_cur_poc_01 G2_DEC_REG(46, 16, 0xff) -#define hevc_cur_poc_02 G2_DEC_REG(46, 8, 0xff) -#define hevc_cur_poc_03 G2_DEC_REG(46, 0, 0xff) - -#define hevc_cur_poc_04 G2_DEC_REG(47, 24, 0xff) -#define hevc_cur_poc_05 G2_DEC_REG(47, 16, 0xff) -#define hevc_cur_poc_06 G2_DEC_REG(47, 8, 0xff) -#define hevc_cur_poc_07 G2_DEC_REG(47, 0, 0xff) - -#define hevc_cur_poc_08 G2_DEC_REG(48, 24, 0xff) -#define hevc_cur_poc_09 G2_DEC_REG(48, 16, 0xff) -#define hevc_cur_poc_10 G2_DEC_REG(48, 8, 0xff) -#define hevc_cur_poc_11 G2_DEC_REG(48, 0, 0xff) - -#define hevc_cur_poc_12 G2_DEC_REG(49, 24, 0xff) -#define hevc_cur_poc_13 G2_DEC_REG(49, 16, 0xff) -#define hevc_cur_poc_14 G2_DEC_REG(49, 8, 0xff) -#define hevc_cur_poc_15 G2_DEC_REG(49, 0, 0xff) - -#define vp9_refpic_seg6 G2_DEC_REG(31, 15, 0x7) -#define vp9_skip_seg6 G2_DEC_REG(31, 14, 0x1) -#define vp9_filt_level_seg6 G2_DEC_REG(31, 8, 0x3f) -#define vp9_quant_seg6 G2_DEC_REG(31, 0, 0xff) - -#define vp9_refpic_seg7 G2_DEC_REG(32, 15, 0x7) -#define vp9_skip_seg7 G2_DEC_REG(32, 14, 0x1) -#define vp9_filt_level_seg7 G2_DEC_REG(32, 8, 0x3f) -#define vp9_quant_seg7 G2_DEC_REG(32, 0, 0xff) - -#define vp9_lref_width G2_DEC_REG(33, 16, 0xffff) -#define vp9_lref_height G2_DEC_REG(33, 0, 0xffff) - -#define vp9_gref_width G2_DEC_REG(34, 16, 0xffff) -#define vp9_gref_height G2_DEC_REG(34, 0, 0xffff) - -#define vp9_aref_width G2_DEC_REG(35, 16, 0xffff) -#define vp9_aref_height G2_DEC_REG(35, 0, 0xffff) - -#define vp9_lref_hor_scale G2_DEC_REG(36, 16, 0xffff) -#define vp9_lref_ver_scale G2_DEC_REG(36, 0, 0xffff) - -#define vp9_gref_hor_scale G2_DEC_REG(37, 16, 0xffff) -#define vp9_gref_ver_scale G2_DEC_REG(37, 0, 0xffff) - -#define vp9_aref_hor_scale G2_DEC_REG(38, 16, 0xffff) -#define vp9_aref_ver_scale G2_DEC_REG(38, 0, 0xffff) - -#define vp9_filt_ref_adj_0 G2_DEC_REG(46, 24, 0x7f) -#define vp9_filt_ref_adj_1 G2_DEC_REG(46, 16, 0x7f) -#define vp9_filt_ref_adj_2 G2_DEC_REG(46, 8, 0x7f) -#define vp9_filt_ref_adj_3 G2_DEC_REG(46, 0, 0x7f) - -#define vp9_filt_mb_adj_0 G2_DEC_REG(47, 24, 0x7f) -#define vp9_filt_mb_adj_1 G2_DEC_REG(47, 16, 0x7f) -#define vp9_filt_mb_adj_2 G2_DEC_REG(47, 8, 0x7f) -#define vp9_filt_mb_adj_3 G2_DEC_REG(47, 0, 0x7f) - -#define g2_apf_threshold G2_DEC_REG(55, 0, 0xffff) - -#define g2_clk_gate_e G2_DEC_REG(58, 16, 0x1) -#define g2_double_buffer_e G2_DEC_REG(58, 15, 0x1) -#define g2_buswidth G2_DEC_REG(58, 8, 0x7) -#define g2_max_burst G2_DEC_REG(58, 0, 0xff) - -#define g2_down_scale_e G2_DEC_REG(184, 7, 0x1) -#define g2_down_scale_y G2_DEC_REG(184, 2, 0x3) -#define g2_down_scale_x G2_DEC_REG(184, 0, 0x3) - -#define G2_REG_CONFIG G2_SWREG(58) -#define G2_REG_CONFIG_DEC_CLK_GATE_E BIT(16) -#define G2_REG_CONFIG_DEC_CLK_GATE_IDLE_E BIT(17) - -#define G2_OUT_LUMA_ADDR (G2_SWREG(65)) -#define G2_REF_LUMA_ADDR(i) (G2_SWREG(67) + ((i) * 0x8)) -#define G2_VP9_SEGMENT_WRITE_ADDR (G2_SWREG(79)) -#define G2_VP9_SEGMENT_READ_ADDR (G2_SWREG(81)) -#define G2_OUT_CHROMA_ADDR (G2_SWREG(99)) -#define G2_REF_CHROMA_ADDR(i) (G2_SWREG(101) + ((i) * 0x8)) -#define G2_OUT_MV_ADDR (G2_SWREG(133)) -#define G2_REF_MV_ADDR(i) (G2_SWREG(135) + ((i) * 0x8)) -#define G2_TILE_SIZES_ADDR (G2_SWREG(167)) -#define G2_STREAM_ADDR (G2_SWREG(169)) -#define G2_HEVC_SCALING_LIST_ADDR (G2_SWREG(171)) -#define G2_VP9_CTX_COUNT_ADDR (G2_SWREG(171)) -#define G2_VP9_PROBS_ADDR (G2_SWREG(173)) -#define G2_RS_OUT_LUMA_ADDR (G2_SWREG(175)) -#define G2_RS_OUT_CHROMA_ADDR (G2_SWREG(177)) -#define G2_TILE_FILTER_ADDR (G2_SWREG(179)) -#define G2_TILE_SAO_ADDR (G2_SWREG(181)) -#define G2_TILE_BSD_ADDR (G2_SWREG(183)) -#define G2_DS_DST (G2_SWREG(186)) -#define G2_DS_DST_CHR (G2_SWREG(188)) - -#define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff) -#define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff) - -#endif diff --git a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c b/drivers/staging/media/hantro/hantro_g2_vp9_dec.c deleted file mode 100644 index 6fc4b555517f..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c +++ /dev/null @@ -1,1014 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ -#include "media/videobuf2-core.h" -#include "media/videobuf2-dma-contig.h" -#include "media/videobuf2-v4l2.h" -#include -#include -#include -#include - -#include "hantro.h" -#include "hantro_vp9.h" -#include "hantro_g2_regs.h" - -#define G2_ALIGN 16 - -enum hantro_ref_frames { - INTRA_FRAME = 0, - LAST_FRAME = 1, - GOLDEN_FRAME = 2, - ALTREF_FRAME = 3, - MAX_REF_FRAMES = 4 -}; - -static int start_prepare_run(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame **dec_params) -{ - const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct v4l2_ctrl *ctrl; - unsigned int fctx_idx; - - /* v4l2-specific stuff */ - hantro_start_prepare_run(ctx); - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_FRAME); - if (WARN_ON(!ctrl)) - return -EINVAL; - *dec_params = ctrl->p_cur.p; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); - if (WARN_ON(!ctrl)) - return -EINVAL; - prob_updates = ctrl->p_cur.p; - vp9_ctx->cur.tx_mode = prob_updates->tx_mode; - - /* - * vp9 stuff - * - * by this point the userspace has done all parts of 6.2 uncompressed_header() - * except this fragment: - * if ( FrameIsIntra || error_resilient_mode ) { - * setup_past_independence ( ) - * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 || - * reset_frame_context == 3 ) { - * for ( i = 0; i < 4; i ++ ) { - * save_probs( i ) - * } - * } else if ( reset_frame_context == 2 ) { - * save_probs( frame_context_idx ) - * } - * frame_context_idx = 0 - * } - */ - fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx->frame_context); - vp9_ctx->cur.frame_context_idx = fctx_idx; - - /* 6.1 frame(sz): load_probs() and load_probs2() */ - vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; - - /* - * The userspace has also performed 6.3 compressed_header(), but handling the - * probs in a special way. All probs which need updating, except MV-related, - * have been read from the bitstream and translated through inv_map_table[], - * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed - * by userspace are either translated values (there are no 0 values in - * inv_map_table[]), or zero to indicate no update. All MV-related probs which need - * updating have been read from the bitstream and (mv_prob << 1) | 1 has been - * performed. The values passed by userspace are either new values - * to replace old ones (the above mentioned shift and bitwise or never result in - * a zero) or zero to indicate no update. - * fw_update_probs() performs actual probs updates or leaves probs as-is - * for values for which a zero was passed from userspace. - */ - v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, *dec_params); - - return 0; -} - -static size_t chroma_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2; - - return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel; -} - -static size_t mv_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - size_t cr_offset = chroma_offset(ctx, dec_params); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - -static struct hantro_decoded_buffer * -get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) -{ - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf; - - /* - * If a ref is unused or invalid, address of current destination - * buffer is returned. - */ - buf = vb2_find_buffer(cap_q, timestamp); - if (!buf) - buf = &dst->vb2_buf; - - return vb2_to_hantro_decoded_buf(buf); -} - -static void update_dec_buf_info(struct hantro_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - buf->vp9.width = dec_params->frame_width_minus_1 + 1; - buf->vp9.height = dec_params->frame_height_minus_1 + 1; - buf->vp9.bit_depth = dec_params->bit_depth; -} - -static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx, - struct hantro_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - vp9_ctx->cur.valid = true; - vp9_ctx->cur.reference_mode = dec_params->reference_mode; - vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; - vp9_ctx->cur.flags = dec_params->flags; - vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; -} - -static void config_output(struct hantro_ctx *ctx, - struct hantro_decoded_buffer *dst, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - dma_addr_t luma_addr, chroma_addr, mv_addr; - - hantro_reg_write(ctx->dev, &g2_out_dis, 0); - if (!ctx->dev->variant->legacy_regs) - hantro_reg_write(ctx->dev, &g2_output_format, 0); - - luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - hantro_write_addr(ctx->dev, G2_OUT_LUMA_ADDR, luma_addr); - - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr); - - mv_addr = luma_addr + mv_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr); -} - -struct hantro_vp9_ref_reg { - const struct hantro_reg width; - const struct hantro_reg height; - const struct hantro_reg hor_scale; - const struct hantro_reg ver_scale; - u32 y_base; - u32 c_base; -}; - -static void config_ref(struct hantro_ctx *ctx, - struct hantro_decoded_buffer *dst, - const struct hantro_vp9_ref_reg *ref_reg, - const struct v4l2_ctrl_vp9_frame *dec_params, - u64 ref_ts) -{ - struct hantro_decoded_buffer *buf; - dma_addr_t luma_addr, chroma_addr; - u32 refw, refh; - - buf = get_ref_buf(ctx, &dst->base.vb, ref_ts); - refw = buf->vp9.width; - refh = buf->vp9.height; - - hantro_reg_write(ctx->dev, &ref_reg->width, refw); - hantro_reg_write(ctx->dev, &ref_reg->height, refh); - - hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst->vp9.width); - hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst->vp9.height); - - luma_addr = hantro_get_dec_buf_addr(ctx, &buf->base.vb.vb2_buf); - hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr); - - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr); -} - -static void config_ref_registers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - struct hantro_decoded_buffer *dst, - struct hantro_decoded_buffer *mv_ref) -{ - static const struct hantro_vp9_ref_reg ref_regs[] = { - { - /* Last */ - .width = vp9_lref_width, - .height = vp9_lref_height, - .hor_scale = vp9_lref_hor_scale, - .ver_scale = vp9_lref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(0), - .c_base = G2_REF_CHROMA_ADDR(0), - }, { - /* Golden */ - .width = vp9_gref_width, - .height = vp9_gref_height, - .hor_scale = vp9_gref_hor_scale, - .ver_scale = vp9_gref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(4), - .c_base = G2_REF_CHROMA_ADDR(4), - }, { - /* Altref */ - .width = vp9_aref_width, - .height = vp9_aref_height, - .hor_scale = vp9_aref_hor_scale, - .ver_scale = vp9_aref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(5), - .c_base = G2_REF_CHROMA_ADDR(5), - }, - }; - dma_addr_t mv_addr; - - config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params->last_frame_ts); - config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params->golden_frame_ts); - config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts); - - mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) + - mv_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr); - - hantro_reg_write(ctx->dev, &vp9_last_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0); - - hantro_reg_write(ctx->dev, &vp9_gref_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0); - - hantro_reg_write(ctx->dev, &vp9_aref_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0); -} - -static void recompute_tile_info(unsigned short *tile_info, unsigned int tiles, unsigned int sbs) -{ - int i; - unsigned int accumulated = 0; - unsigned int next_accumulated; - - for (i = 1; i <= tiles; ++i) { - next_accumulated = i * sbs / tiles; - *tile_info++ = next_accumulated - accumulated; - accumulated = next_accumulated; - } -} - -static void -recompute_tile_rc_info(struct hantro_ctx *ctx, - unsigned int tile_r, unsigned int tile_c, - unsigned int sbs_r, unsigned int sbs_c) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - - recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r); - recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c); - - vp9_ctx->last_tile_r = tile_r; - vp9_ctx->last_tile_c = tile_c; - vp9_ctx->last_sbs_r = sbs_r; - vp9_ctx->last_sbs_c = sbs_c; -} - -static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int sbs_r) -{ - if (tile_r == sbs_r + 1) - return 1; - - if (tile_r == sbs_r + 2) - return 2; - - return 0; -} - -static void -fill_tile_info(struct hantro_ctx *ctx, - unsigned int tile_r, unsigned int tile_c, - unsigned int sbs_r, unsigned int sbs_c, - unsigned short *tile_mem) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - unsigned int i, j; - bool first = true; - - for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) { - unsigned short r_info = vp9_ctx->tile_r_info[i]; - - if (first) { - if (i > 0) - r_info += vp9_ctx->tile_r_info[0]; - if (i == 2) - r_info += vp9_ctx->tile_r_info[1]; - first = false; - } - for (j = 0; j < tile_c; ++j) { - *tile_mem++ = vp9_ctx->tile_c_info[j]; - *tile_mem++ = r_info; - } - } -} - -static void -config_tiles(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - struct hantro_decoded_buffer *dst) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_ctx->misc; - struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge; - dma_addr_t addr; - unsigned short *tile_mem; - unsigned int rows, cols; - - addr = misc->dma + vp9_ctx->tile_info_offset; - hantro_write_addr(ctx->dev, G2_TILE_SIZES_ADDR, addr); - - tile_mem = misc->cpu + vp9_ctx->tile_info_offset; - if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) { - unsigned int tile_r = (1 << dec_params->tile_rows_log2); - unsigned int tile_c = (1 << dec_params->tile_cols_log2); - unsigned int sbs_r = hantro_vp9_num_sbs(dst->vp9.height); - unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width); - - if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx->last_tile_c || - sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx->last_sbs_c) - recompute_tile_rc_info(ctx, tile_r, tile_c, sbs_r, sbs_c); - - fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, tile_mem); - - cols = tile_c; - rows = tile_r; - hantro_reg_write(ctx->dev, &g2_tile_e, 1); - } else { - tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width); - tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height); - - cols = 1; - rows = 1; - hantro_reg_write(ctx->dev, &g2_tile_e, 0); - } - - if (ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_num_tile_cols_old, cols); - hantro_reg_write(ctx->dev, &g2_num_tile_rows_old, rows); - } else { - hantro_reg_write(ctx->dev, &g2_num_tile_cols, cols); - hantro_reg_write(ctx->dev, &g2_num_tile_rows, rows); - } - - /* provide aux buffers even if no tiles are used */ - addr = tile_edge->dma; - hantro_write_addr(ctx->dev, G2_TILE_FILTER_ADDR, addr); - - addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset; - hantro_write_addr(ctx->dev, G2_TILE_BSD_ADDR, addr); -} - -static void -update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx, - const struct v4l2_vp9_segmentation *seg, - unsigned int feature, - unsigned int segid) -{ - u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature); - - vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid][feature]; - vp9_ctx->feature_enabled[segid] &= ~mask; - vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & mask); -} - -static inline s16 clip3(s16 x, s16 y, s16 z) -{ - return (z < x) ? x : (z > y) ? y : z; -} - -static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 clip) -{ - if (absolute) - return feature_data; - - return clip3(0, 255, feat_val + feature_data); -} - -static void config_segment(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - const struct v4l2_vp9_segmentation *seg; - s16 feat_val; - unsigned char feat_id; - unsigned int segid; - bool segment_enabled, absolute, update_data; - - static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = { - { vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, vp9_skip_seg0 }, - { vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, vp9_skip_seg1 }, - { vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, vp9_skip_seg2 }, - { vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, vp9_skip_seg3 }, - { vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, vp9_skip_seg4 }, - { vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, vp9_skip_seg5 }, - { vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, vp9_skip_seg6 }, - { vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, vp9_skip_seg7 }, - }; - - segment_enabled = !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED); - hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled); - hantro_reg_write(ctx->dev, &vp9_segment_upd_e, - !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP)); - hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e, - !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE)); - - seg = &dec_params->seg; - absolute = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE); - update_data = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA); - - for (segid = 0; segid < 8; ++segid) { - /* Quantizer segment feature */ - feat_id = V4L2_VP9_SEG_LVL_ALT_Q; - feat_val = dec_params->quant.base_q_idx; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = feat_val_clip3(feat_val, - vp9_ctx->feature_data[segid][feat_id], - absolute, 255); - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Loop filter segment feature */ - feat_id = V4L2_VP9_SEG_LVL_ALT_L; - feat_val = dec_params->lf.level; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = feat_val_clip3(feat_val, - vp9_ctx->feature_data[segid][feat_id], - absolute, 63); - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Reference frame segment feature */ - feat_id = V4L2_VP9_SEG_LVL_REF_FRAME; - feat_val = 0; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (!(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = vp9_ctx->feature_data[segid][feat_id] + 1; - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Skip segment feature */ - feat_id = V4L2_VP9_SEG_LVL_SKIP; - feat_val = 0; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, - feat_id, segid) ? 1 : 0; - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - } -} - -static void config_loop_filter(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - bool d = dec_params->lf.flags & V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED; - - hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level); - hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params->lf.level == 0); - hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params->lf.sharpness); - - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params->lf.ref_deltas[0] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params->lf.ref_deltas[1] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params->lf.ref_deltas[2] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params->lf.ref_deltas[3] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params->lf.mode_deltas[0] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params->lf.mode_deltas[1] : 0); -} - -static void config_picture_dimensions(struct hantro_ctx *ctx, struct hantro_decoded_buffer *dst) -{ - u32 pic_w_4x4, pic_h_4x4; - - hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 7) / 8); - hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height + 7) / 8); - pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2; - pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2; - hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4); - hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4); -} - -static void -config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - if (ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_bit_depth_y, dec_params->bit_depth); - hantro_reg_write(ctx->dev, &g2_bit_depth_c, dec_params->bit_depth); - hantro_reg_write(ctx->dev, &g2_pix_shift, 0); - } else { - hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params->bit_depth - 8); - hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params->bit_depth - 8); - } -} - -static inline bool is_lossless(const struct v4l2_vp9_quantization *quant) -{ - return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 && - quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0; -} - -static void -config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params->quant.delta_q_y_dc); - hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params->quant.delta_q_uv_dc); - hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params->quant.delta_q_uv_ac); - hantro_reg_write(ctx->dev, &vp9_lossless_e, is_lossless(&dec_params->quant)); -} - -static u32 -hantro_interp_filter_from_v4l2(unsigned int interpolation_filter) -{ - switch (interpolation_filter) { - case V4L2_VP9_INTERP_FILTER_EIGHTTAP: - return 0x1; - case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH: - return 0; - case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP: - return 0x2; - case V4L2_VP9_INTERP_FILTER_BILINEAR: - return 0x3; - case V4L2_VP9_INTERP_FILTER_SWITCHABLE: - return 0x4; - } - - return 0; -} - -static void -config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - bool intra_only, bool resolution_change) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - - hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only); - - hantro_reg_write(ctx->dev, &vp9_transform_mode, vp9_ctx->cur.tx_mode); - - hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ? - 0 : hantro_interp_filter_from_v4l2(dec_params->interpolation_filter)); - - hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e, - !!(dec_params->flags & V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV)); - - hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params->reference_mode); - - hantro_reg_write(ctx->dev, &g2_tempor_mvp_e, - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - !(vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_INTRA_ONLY) && - !resolution_change && - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME - ); - - hantro_reg_write(ctx->dev, &g2_write_mvs_e, - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME)); -} - -static void -config_compound_reference(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - u32 comp_fixed_ref, comp_var_ref[2]; - bool last_ref_frame_sign_bias; - bool golden_ref_frame_sign_bias; - bool alt_ref_frame_sign_bias; - bool comp_ref_allowed = 0; - - comp_fixed_ref = 0; - comp_var_ref[0] = 0; - comp_var_ref[1] = 0; - - last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST; - golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN; - alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT; - - /* 6.3.12 Frame reference mode syntax */ - comp_ref_allowed |= golden_ref_frame_sign_bias != last_ref_frame_sign_bias; - comp_ref_allowed |= alt_ref_frame_sign_bias != last_ref_frame_sign_bias; - - if (comp_ref_allowed) { - if (last_ref_frame_sign_bias == - golden_ref_frame_sign_bias) { - comp_fixed_ref = ALTREF_FRAME; - comp_var_ref[0] = LAST_FRAME; - comp_var_ref[1] = GOLDEN_FRAME; - } else if (last_ref_frame_sign_bias == - alt_ref_frame_sign_bias) { - comp_fixed_ref = GOLDEN_FRAME; - comp_var_ref[0] = LAST_FRAME; - comp_var_ref[1] = ALTREF_FRAME; - } else { - comp_fixed_ref = LAST_FRAME; - comp_var_ref[0] = GOLDEN_FRAME; - comp_var_ref[1] = ALTREF_FRAME; - } - } - - hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref); - hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, comp_var_ref[0]); - hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, comp_var_ref[1]); -} - -#define INNER_LOOP \ -do { \ - for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) { \ - memcpy(adaptive->coef[i][j][k][l][m], \ - probs->coef[i][j][k][l][m], \ - sizeof(probs->coef[i][j][k][l][m])); \ - \ - adaptive->coef[i][j][k][l][m][3] = 0; \ - } \ -} while (0) - -static void config_probs(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_ctx->misc; - struct hantro_g2_all_probs *all_probs = misc->cpu; - struct hantro_g2_probs *adaptive; - struct hantro_g2_mv_probs *mv; - const struct v4l2_vp9_segmentation *seg = &dec_params->seg; - const struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; - int i, j, k, l, m; - - for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i) - for (j = 0; j < ARRAY_SIZE(all_probs->kf_y_mode_prob[0]); ++j) { - memcpy(all_probs->kf_y_mode_prob[i][j], - v4l2_vp9_kf_y_mode_prob[i][j], - ARRAY_SIZE(all_probs->kf_y_mode_prob[i][j])); - - all_probs->kf_y_mode_prob_tail[i][j][0] = - v4l2_vp9_kf_y_mode_prob[i][j][8]; - } - - memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs, - sizeof(all_probs->mb_segment_tree_probs)); - - memcpy(all_probs->segment_pred_probs, seg->pred_probs, - sizeof(all_probs->segment_pred_probs)); - - for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) { - memcpy(all_probs->kf_uv_mode_prob[i], v4l2_vp9_kf_uv_mode_prob[i], - ARRAY_SIZE(all_probs->kf_uv_mode_prob[i])); - - all_probs->kf_uv_mode_prob_tail[i][0] = v4l2_vp9_kf_uv_mode_prob[i][8]; - } - - adaptive = &all_probs->probs; - - for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) { - memcpy(adaptive->inter_mode[i], probs->inter_mode[i], - ARRAY_SIZE(probs->inter_mode[i])); - - adaptive->inter_mode[i][3] = 0; - } - - memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive->is_inter)); - - for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) { - memcpy(adaptive->uv_mode[i], probs->uv_mode[i], - sizeof(adaptive->uv_mode[i])); - adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8]; - } - - memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8)); - memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16)); - memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32)); - - for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) { - memcpy(adaptive->y_mode[i], probs->y_mode[i], - ARRAY_SIZE(adaptive->y_mode[i])); - - adaptive->y_mode_tail[i][0] = probs->y_mode[i][8]; - } - - for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) { - memcpy(adaptive->partition[0][i], v4l2_vp9_kf_partition_probs[i], - sizeof(v4l2_vp9_kf_partition_probs[i])); - - adaptive->partition[0][i][3] = 0; - } - - for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) { - memcpy(adaptive->partition[1][i], probs->partition[i], - sizeof(probs->partition[i])); - - adaptive->partition[1][i][3] = 0; - } - - memcpy(adaptive->interp_filter, probs->interp_filter, - sizeof(adaptive->interp_filter)); - - memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive->comp_mode)); - - memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip)); - - mv = &adaptive->mv; - - memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint)); - memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign)); - memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv->class0_bit)); - memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr)); - memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp)); - memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp)); - memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes)); - memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr)); - memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits)); - - memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive->single_ref)); - - memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive->comp_ref)); - - for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i) - for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j) - for (k = 0; k < ARRAY_SIZE(adaptive->coef[0][0]); ++k) - for (l = 0; l < ARRAY_SIZE(adaptive->coef[0][0][0]); ++l) - INNER_LOOP; - - hantro_write_addr(ctx->dev, G2_VP9_PROBS_ADDR, misc->dma); -} - -static void config_counts(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_dec->misc; - dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset; - - hantro_write_addr(ctx->dev, G2_VP9_CTX_COUNT_ADDR, addr); -} - -static void config_seg_map(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - bool intra_only, bool update_map) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map; - dma_addr_t addr; - - if (intra_only || - (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) { - memset(segment_map->cpu, 0, segment_map->size); - memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx->feature_data)); - memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx->feature_enabled)); - } - - addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx->segment_map_size; - hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_READ_ADDR, addr); - - addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx->segment_map_size; - hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_WRITE_ADDR, addr); - - if (update_map) - vp9_ctx->active_segment = 1 - vp9_ctx->active_segment; -} - -static void -config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - struct vb2_v4l2_buffer *vb2_src) -{ - dma_addr_t stream_base, tmp_addr; - unsigned int headres_size; - u32 src_len, start_bit, src_buf_len; - - headres_size = dec_params->uncompressed_header_size - + dec_params->compressed_header_size; - - stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - tmp_addr = stream_base + headres_size; - if (ctx->dev->variant->legacy_regs) - hantro_write_addr(ctx->dev, G2_STREAM_ADDR, (tmp_addr & ~0xf)); - else - hantro_write_addr(ctx->dev, G2_STREAM_ADDR, stream_base); - - start_bit = (tmp_addr & 0xf) * 8; - hantro_reg_write(ctx->dev, &g2_start_bit, start_bit); - - src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0); - src_len += start_bit / 8 - headres_size; - hantro_reg_write(ctx->dev, &g2_stream_len, src_len); - - if (!ctx->dev->variant->legacy_regs) { - tmp_addr &= ~0xf; - hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - stream_base); - src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0); - hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len); - } -} - -static void -config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_decoded_buffer *dst, *last, *mv_ref; - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - const struct v4l2_vp9_segmentation *seg; - bool intra_only, resolution_change; - - /* vp9 stuff */ - dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); - - if (vp9_ctx->last.valid) - last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp); - else - last = dst; - - update_dec_buf_info(dst, dec_params); - update_ctx_cur_info(vp9_ctx, dst, dec_params); - seg = &dec_params->seg; - - intra_only = !!(dec_params->flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | - V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); - - if (!intra_only && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - vp9_ctx->last.valid) - mv_ref = last; - else - mv_ref = dst; - - resolution_change = dst->vp9.width != last->vp9.width || - dst->vp9.height != last->vp9.height; - - /* configure basic registers */ - hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE); - if (!ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1); - } else { - hantro_reg_write(ctx->dev, &g2_strm_swap_old, 0x1f); - hantro_reg_write(ctx->dev, &g2_pic_swap, 0x10); - hantro_reg_write(ctx->dev, &g2_dirmv_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab0_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab1_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab2_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab3_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_rscan_swap, 0x10); - } - hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128); - hantro_reg_write(ctx->dev, &g2_max_burst, 16); - hantro_reg_write(ctx->dev, &g2_apf_threshold, 8); - hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1); - hantro_reg_write(ctx->dev, &g2_max_cb_size, 6); - hantro_reg_write(ctx->dev, &g2_min_cb_size, 3); - if (ctx->dev->variant->double_buffer) - hantro_reg_write(ctx->dev, &g2_double_buffer_e, 1); - - config_output(ctx, dst, dec_params); - - if (!intra_only) - config_ref_registers(ctx, dec_params, dst, mv_ref); - - config_tiles(ctx, dec_params, dst); - config_segment(ctx, dec_params); - config_loop_filter(ctx, dec_params); - config_picture_dimensions(ctx, dst); - config_bit_depth(ctx, dec_params); - config_quant(ctx, dec_params); - config_others(ctx, dec_params, intra_only, resolution_change); - config_compound_reference(ctx, dec_params); - config_probs(ctx, dec_params); - config_counts(ctx); - config_seg_map(ctx, dec_params, intra_only, - seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP); - config_source(ctx, dec_params, vb2_src); -} - -int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp9_frame *decode_params; - struct vb2_v4l2_buffer *src; - struct vb2_v4l2_buffer *dst; - int ret; - - hantro_g2_check_idle(ctx->dev); - - ret = start_prepare_run(ctx, &decode_params); - if (ret) { - hantro_end_prepare_run(ctx); - return ret; - } - - src = hantro_get_src_buf(ctx); - dst = hantro_get_dst_buf(ctx); - - config_registers(ctx, decode_params, src, dst); - - hantro_end_prepare_run(ctx); - - vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); - - return 0; -} - -#define copy_tx_and_skip(p1, p2) \ -do { \ - memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ - memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ - memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ - memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ -} while (0) - -void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - unsigned int fctx_idx; - - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) - goto out_update_last; - - fctx_idx = vp9_ctx->cur.frame_context_idx; - - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { - /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ - struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; - bool frame_is_intra = vp9_ctx->cur.flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); - struct tx_and_skip { - u8 tx8[2][1]; - u8 tx16[2][2]; - u8 tx32[2][3]; - u8 skip[3]; - } _tx_skip, *tx_skip = &_tx_skip; - struct v4l2_vp9_frame_symbol_counts *counts; - struct symbol_counts *hantro_cnts; - u32 tx16p[2][4]; - int i; - - /* buffer the forward-updated TX and skip probs */ - if (frame_is_intra) - copy_tx_and_skip(tx_skip, probs); - - /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ - *probs = vp9_ctx->frame_context[fctx_idx]; - - /* if FrameIsIntra then undo the effect of load_probs2() */ - if (frame_is_intra) - copy_tx_and_skip(probs, tx_skip); - - counts = &vp9_ctx->cnts; - hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; - for (i = 0; i < ARRAY_SIZE(tx16p); ++i) { - memcpy(tx16p[i], - hantro_cnts->tx16x16_count[i], - sizeof(hantro_cnts->tx16x16_count[0])); - tx16p[i][3] = 0; - } - counts->tx16p = &tx16p; - - v4l2_vp9_adapt_coef_probs(probs, counts, - !vp9_ctx->last.valid || - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, - frame_is_intra); - - if (!frame_is_intra) { - /* load_probs2() already done */ - u32 mv_mode[7][4]; - - for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) { - mv_mode[i][0] = hantro_cnts->inter_mode_counts[i][1][0]; - mv_mode[i][1] = hantro_cnts->inter_mode_counts[i][2][0]; - mv_mode[i][2] = hantro_cnts->inter_mode_counts[i][0][0]; - mv_mode[i][3] = hantro_cnts->inter_mode_counts[i][2][1]; - } - counts->mv_mode = &mv_mode; - v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, - vp9_ctx->cur.reference_mode, - vp9_ctx->cur.interpolation_filter, - vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); - } - } - - vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; - -out_update_last: - vp9_ctx->last = vp9_ctx->cur; -} diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c deleted file mode 100644 index 12d69503d6ba..000000000000 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include -#include -#include "hantro_jpeg.h" -#include "hantro.h" -#include "hantro_v4l2.h" -#include "hantro_hw.h" -#include "hantro_h1_regs.h" - -#define H1_JPEG_QUANT_TABLE_COUNT 16 - -static void hantro_h1_set_src_img_ctrl(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - u32 overfill_r, overfill_b; - u32 reg; - - /* - * The format width and height are already macroblock aligned - * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination - * format width and height can be further modified by - * .vidioc_s_selection(), and the width is 4-aligned. - */ - overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; - overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; - - reg = H1_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width) - | H1_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) - | H1_REG_IN_IMG_CTRL_OVRFLB(overfill_b) - | H1_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); - vepu_write_relaxed(vpu, reg, H1_REG_IN_IMG_CTRL); -} - -static void hantro_h1_jpeg_enc_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf) -{ - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; - dma_addr_t src[3]; - u32 size_left; - - size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; - if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) - size_left = 0; - - WARN_ON(pix_fmt->num_planes > 3); - - vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + - ctx->vpu_dst_fmt->header_size, - H1_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, size_left, H1_REG_STR_BUF_LIMIT); - - if (pix_fmt->num_planes == 1) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - /* single plane formats we supported are all interlaced */ - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - } else if (pix_fmt->num_planes == 2) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); - } else { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); - vepu_write_relaxed(vpu, src[2], H1_REG_ADDR_IN_PLANE_2); - } -} - -static void -hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, - unsigned char *luma_qtable, - unsigned char *chroma_qtable) -{ - u32 reg, i; - __be32 *luma_qtable_p; - __be32 *chroma_qtable_p; - - luma_qtable_p = (__be32 *)luma_qtable; - chroma_qtable_p = (__be32 *)chroma_qtable; - - /* - * Quantization table registers must be written in contiguous blocks. - * DO NOT collapse the below two "for" loops into one. - */ - for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable_p[i]); - vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); - } - - for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&chroma_qtable_p[i]); - vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); - } -} - -int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct hantro_jpeg_ctx jpeg_ctx; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - 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; - hantro_jpeg_header_assemble(&jpeg_ctx); - - /* Switch to JPEG encoder mode before writing registers */ - vepu_write_relaxed(vpu, H1_REG_ENC_CTRL_ENC_MODE_JPEG, - H1_REG_ENC_CTRL); - - hantro_h1_set_src_img_ctrl(vpu, ctx); - hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf); - hantro_h1_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, - jpeg_ctx.hw_chroma_qtable); - - reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 - | H1_REG_AXI_CTRL_INPUT_SWAP16 - | H1_REG_AXI_CTRL_BURST_LEN(16) - | H1_REG_AXI_CTRL_OUTPUT_SWAP32 - | H1_REG_AXI_CTRL_INPUT_SWAP32 - | H1_REG_AXI_CTRL_OUTPUT_SWAP8 - | H1_REG_AXI_CTRL_INPUT_SWAP8; - /* Make sure that all registers are written at this point. */ - vepu_write(vpu, reg, H1_REG_AXI_CTRL); - - reg = H1_REG_ENC_CTRL_WIDTH(MB_WIDTH(ctx->src_fmt.width)) - | H1_REG_ENC_CTRL_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) - | H1_REG_ENC_CTRL_ENC_MODE_JPEG - | H1_REG_ENC_PIC_INTRA - | H1_REG_ENC_CTRL_EN_BIT; - - hantro_end_prepare_run(ctx); - - vepu_write(vpu, reg, H1_REG_ENC_CTRL); - - return 0; -} - -void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; - struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - ctx->vpu_dst_fmt->header_size + bytesused); -} diff --git a/drivers/staging/media/hantro/hantro_h1_regs.h b/drivers/staging/media/hantro/hantro_h1_regs.h deleted file mode 100644 index 30e7e7b920b5..000000000000 --- a/drivers/staging/media/hantro/hantro_h1_regs.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa - */ - -#ifndef HANTRO_H1_REGS_H_ -#define HANTRO_H1_REGS_H_ - -/* Encoder registers. */ -#define H1_REG_INTERRUPT 0x004 -#define H1_REG_INTERRUPT_FRAME_RDY BIT(2) -#define H1_REG_INTERRUPT_DIS_BIT BIT(1) -#define H1_REG_INTERRUPT_BIT BIT(0) -#define H1_REG_AXI_CTRL 0x008 -#define H1_REG_AXI_CTRL_OUTPUT_SWAP16 BIT(15) -#define H1_REG_AXI_CTRL_INPUT_SWAP16 BIT(14) -#define H1_REG_AXI_CTRL_BURST_LEN(x) ((x) << 8) -#define H1_REG_AXI_CTRL_GATE_BIT BIT(4) -#define H1_REG_AXI_CTRL_OUTPUT_SWAP32 BIT(3) -#define H1_REG_AXI_CTRL_INPUT_SWAP32 BIT(2) -#define H1_REG_AXI_CTRL_OUTPUT_SWAP8 BIT(1) -#define H1_REG_AXI_CTRL_INPUT_SWAP8 BIT(0) -#define H1_REG_ADDR_OUTPUT_STREAM 0x014 -#define H1_REG_ADDR_OUTPUT_CTRL 0x018 -#define H1_REG_ADDR_REF_LUMA 0x01c -#define H1_REG_ADDR_REF_CHROMA 0x020 -#define H1_REG_ADDR_REC_LUMA 0x024 -#define H1_REG_ADDR_REC_CHROMA 0x028 -#define H1_REG_ADDR_IN_PLANE_0 0x02c -#define H1_REG_ADDR_IN_PLANE_1 0x030 -#define H1_REG_ADDR_IN_PLANE_2 0x034 -#define H1_REG_ENC_CTRL 0x038 -#define H1_REG_ENC_CTRL_TIMEOUT_EN BIT(31) -#define H1_REG_ENC_CTRL_NAL_MODE_BIT BIT(29) -#define H1_REG_ENC_CTRL_WIDTH(w) ((w) << 19) -#define H1_REG_ENC_CTRL_HEIGHT(h) ((h) << 10) -#define H1_REG_ENC_PIC_INTER (0x0 << 3) -#define H1_REG_ENC_PIC_INTRA (0x1 << 3) -#define H1_REG_ENC_PIC_MVCINTER (0x2 << 3) -#define H1_REG_ENC_CTRL_ENC_MODE_H264 (0x3 << 1) -#define H1_REG_ENC_CTRL_ENC_MODE_JPEG (0x2 << 1) -#define H1_REG_ENC_CTRL_ENC_MODE_VP8 (0x1 << 1) -#define H1_REG_ENC_CTRL_EN_BIT BIT(0) -#define H1_REG_IN_IMG_CTRL 0x03c -#define H1_REG_IN_IMG_CTRL_ROW_LEN(x) ((x) << 12) -#define H1_REG_IN_IMG_CTRL_OVRFLR_D4(x) ((x) << 10) -#define H1_REG_IN_IMG_CTRL_OVRFLB(x) ((x) << 6) -#define H1_REG_IN_IMG_CTRL_FMT(x) ((x) << 2) -#define H1_REG_ENC_CTRL0 0x040 -#define H1_REG_ENC_CTRL0_INIT_QP(x) ((x) << 26) -#define H1_REG_ENC_CTRL0_SLICE_ALPHA(x) ((x) << 22) -#define H1_REG_ENC_CTRL0_SLICE_BETA(x) ((x) << 18) -#define H1_REG_ENC_CTRL0_CHROMA_QP_OFFSET(x) ((x) << 13) -#define H1_REG_ENC_CTRL0_FILTER_DIS(x) ((x) << 5) -#define H1_REG_ENC_CTRL0_IDR_PICID(x) ((x) << 1) -#define H1_REG_ENC_CTRL0_CONSTR_INTRA_PRED BIT(0) -#define H1_REG_ENC_CTRL1 0x044 -#define H1_REG_ENC_CTRL1_PPS_ID(x) ((x) << 24) -#define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16) -#define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x)) -#define H1_REG_ENC_CTRL2 0x048 -#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30) -#define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23) -#define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22) -#define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21) -#define H1_REG_ENC_CTRL2_CABAC_INIT_IDC(x) ((x) << 19) -#define H1_REG_ENC_CTRL2_ENTROPY_CODING_MODE BIT(18) -#define H1_REG_ENC_CTRL2_H264_INTER4X4_MODE BIT(17) -#define H1_REG_ENC_CTRL2_H264_STREAM_MODE BIT(16) -#define H1_REG_ENC_CTRL2_INTRA16X16_MODE(x) ((x)) -#define H1_REG_ENC_CTRL3 0x04c -#define H1_REG_ENC_CTRL3_MUTIMV_EN BIT(30) -#define H1_REG_ENC_CTRL3_MV_PENALTY_1_4P(x) ((x) << 20) -#define H1_REG_ENC_CTRL3_MV_PENALTY_4P(x) ((x) << 10) -#define H1_REG_ENC_CTRL3_MV_PENALTY_1P(x) ((x)) -#define H1_REG_ENC_CTRL4 0x050 -#define H1_REG_ENC_CTRL4_MV_PENALTY_16X8_8X16(x) ((x) << 20) -#define H1_REG_ENC_CTRL4_MV_PENALTY_8X8(x) ((x) << 10) -#define H1_REG_ENC_CTRL4_8X4_4X8(x) ((x)) -#define H1_REG_ENC_CTRL5 0x054 -#define H1_REG_ENC_CTRL5_MACROBLOCK_PENALTY(x) ((x) << 24) -#define H1_REG_ENC_CTRL5_COMPLETE_SLICES(x) ((x) << 16) -#define H1_REG_ENC_CTRL5_INTER_MODE(x) ((x)) -#define H1_REG_STR_HDR_REM_MSB 0x058 -#define H1_REG_STR_HDR_REM_LSB 0x05c -#define H1_REG_STR_BUF_LIMIT 0x060 -#define H1_REG_MAD_CTRL 0x064 -#define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28) -#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22) -#define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x)) -#define H1_REG_ADDR_VP8_PROB_CNT 0x068 -#define H1_REG_QP_VAL 0x06c -#define H1_REG_QP_VAL_LUM(x) ((x) << 26) -#define H1_REG_QP_VAL_MAX(x) ((x) << 20) -#define H1_REG_QP_VAL_MIN(x) ((x) << 14) -#define H1_REG_QP_VAL_CHECKPOINT_DISTAN(x) ((x)) -#define H1_REG_VP8_QP_VAL(i) (0x06c + ((i) * 0x4)) -#define H1_REG_CHECKPOINT(i) (0x070 + ((i) * 0x4)) -#define H1_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) -#define H1_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) -#define H1_REG_CHECKPOINT_RESULT(x) ((((x) >> (16 - 16 \ - * (i & 1))) & 0xffff) \ - * 32) -#define H1_REG_CHKPT_WORD_ERR(i) (0x084 + ((i) * 0x4)) -#define H1_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) -#define H1_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) -#define H1_REG_VP8_BOOL_ENC 0x08c -#define H1_REG_CHKPT_DELTA_QP 0x090 -#define H1_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) -#define H1_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) -#define H1_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) -#define H1_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) -#define H1_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) -#define H1_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) -#define H1_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) -#define H1_REG_VP8_CTRL0 0x090 -#define H1_REG_RLC_CTRL 0x094 -#define H1_REG_RLC_CTRL_STR_OFFS_SHIFT 23 -#define H1_REG_RLC_CTRL_STR_OFFS_MASK (0x3f << 23) -#define H1_REG_RLC_CTRL_RLC_SUM(x) ((x)) -#define H1_REG_MB_CTRL 0x098 -#define H1_REG_MB_CNT_OUT(x) (((x) & 0xffff)) -#define H1_REG_MB_CNT_SET(x) (((x) & 0xffff) << 16) -#define H1_REG_ADDR_NEXT_PIC 0x09c -#define H1_REG_JPEG_LUMA_QUAT(i) (0x100 + ((i) * 0x4)) -#define H1_REG_JPEG_CHROMA_QUAT(i) (0x140 + ((i) * 0x4)) -#define H1_REG_STABILIZATION_OUTPUT 0x0A0 -#define H1_REG_ADDR_CABAC_TBL 0x0cc -#define H1_REG_ADDR_MV_OUT 0x0d0 -#define H1_REG_RGB_YUV_COEFF(i) (0x0d4 + ((i) * 0x4)) -#define H1_REG_RGB_MASK_MSB 0x0dc -#define H1_REG_INTRA_AREA_CTRL 0x0e0 -#define H1_REG_CIR_INTRA_CTRL 0x0e4 -#define H1_REG_INTRA_SLICE_BITMAP(i) (0x0e8 + ((i) * 0x4)) -#define H1_REG_ADDR_VP8_DCT_PART(i) (0x0e8 + ((i) * 0x4)) -#define H1_REG_FIRST_ROI_AREA 0x0f0 -#define H1_REG_SECOND_ROI_AREA 0x0f4 -#define H1_REG_MVC_CTRL 0x0f8 -#define H1_REG_MVC_CTRL_MV16X16_FAVOR(x) ((x) << 28) -#define H1_REG_VP8_INTRA_PENALTY(i) (0x100 + ((i) * 0x4)) -#define H1_REG_ADDR_VP8_SEG_MAP 0x11c -#define H1_REG_VP8_SEG_QP(i) (0x120 + ((i) * 0x4)) -#define H1_REG_DMV_4P_1P_PENALTY(i) (0x180 + ((i) * 0x4)) -#define H1_REG_DMV_4P_1P_PENALTY_BIT(x, i) ((x) << (i) * 8) -#define H1_REG_DMV_QPEL_PENALTY(i) (0x200 + ((i) * 0x4)) -#define H1_REG_DMV_QPEL_PENALTY_BIT(x, i) ((x) << (i) * 8) -#define H1_REG_VP8_CTRL1 0x280 -#define H1_REG_VP8_BIT_COST_GOLDEN 0x284 -#define H1_REG_VP8_LOOP_FLT_DELTA(i) (0x288 + ((i) * 0x4)) - -#endif /* HANTRO_H1_REGS_H_ */ diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c deleted file mode 100644 index 4e9a0ecf5c13..000000000000 --- a/drivers/staging/media/hantro/hantro_h264.c +++ /dev/null @@ -1,521 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong - * Herman Chen - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - */ - -#include -#include -#include - -#include "hantro.h" -#include "hantro_hw.h" - -/* Size with u32 units. */ -#define CABAC_INIT_BUFFER_SIZE (460 * 2) -#define POC_BUFFER_SIZE 34 -#define SCALING_LIST_SIZE (6 * 16 + 2 * 64) - -/* - * For valid and long term reference marking, index are reversed, so bit 31 - * indicates the status of the picture 0. - */ -#define REF_BIT(i) BIT(32 - 1 - (i)) - -/* Data structure describing auxiliary buffer format. */ -struct hantro_h264_dec_priv_tbl { - u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; - u32 poc[POC_BUFFER_SIZE]; - u8 scaling_list[SCALING_LIST_SIZE]; -}; - -/* - * Constant CABAC table. - * From drivers/media/platform/rk3288-vpu/rk3288_vpu_hw_h264d.c - * in https://chromium.googlesource.com/chromiumos/third_party/kernel, - * chromeos-3.14 branch. - */ -static const u32 h264_cabac_table[] = { - 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07330000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x000b0137, - 0x0045ef7f, 0xf3660052, 0xf94aeb6b, 0xe57fe17f, 0xe87fee5f, 0xe57feb72, - 0xe27fef7b, 0xf473f07a, 0xf573f43f, 0xfe44f154, 0xf368fd46, 0xf85df65a, - 0xe27fff4a, 0xfa61f95b, 0xec7ffc38, 0xfb52f94c, 0xea7df95d, 0xf557fd4d, - 0xfb47fc3f, 0xfc44f454, 0xf93ef941, 0x083d0538, 0xfe420140, 0x003dfe4e, - 0x01320734, 0x0a23002c, 0x0b26012d, 0x002e052c, 0x1f110133, 0x07321c13, - 0x10210e3e, 0xf36cf164, 0xf365f35b, 0xf45ef658, 0xf054f656, 0xf953f357, - 0xed5e0146, 0x0048fb4a, 0x123bf866, 0xf164005f, 0xfc4b0248, 0xf54bfd47, - 0x0f2ef345, 0x003e0041, 0x1525f148, 0x09391036, 0x003e0c48, 0x18000f09, - 0x08190d12, 0x0f090d13, 0x0a250c12, 0x061d1421, 0x0f1e042d, 0x013a003e, - 0x073d0c26, 0x0b2d0f27, 0x0b2a0d2c, 0x102d0c29, 0x0a311e22, 0x122a0a37, - 0x1133112e, 0x00591aed, 0x16ef1aef, 0x1ee71cec, 0x21e925e5, 0x21e928e4, - 0x26ef21f5, 0x28f129fa, 0x26012911, 0x1efa1b03, 0x1a1625f0, 0x23fc26f8, - 0x26fd2503, 0x26052a00, 0x23102716, 0x0e301b25, 0x153c0c44, 0x0261fd47, - 0xfa2afb32, 0xfd36fe3e, 0x003a013f, 0xfe48ff4a, 0xf75bfb43, 0xfb1bfd27, - 0xfe2c002e, 0xf040f844, 0xf64efa4d, 0xf656f45c, 0xf137f63c, 0xfa3efc41, - 0xf449f84c, 0xf950f758, 0xef6ef561, 0xec54f54f, 0xfa49fc4a, 0xf356f360, - 0xf561ed75, 0xf84efb21, 0xfc30fe35, 0xfd3ef347, 0xf64ff456, 0xf35af261, - 0x0000fa5d, 0xfa54f84f, 0x0042ff47, 0x003efe3c, 0xfe3bfb4b, 0xfd3efc3a, - 0xf742ff4f, 0x00470344, 0x0a2cf93e, 0x0f240e28, 0x101b0c1d, 0x012c1424, - 0x1220052a, 0x01300a3e, 0x112e0940, 0xf468f561, 0xf060f958, 0xf855f955, - 0xf755f358, 0x0442fd4d, 0xfd4cfa4c, 0x0a3aff4c, 0xff53f963, 0xf25f025f, - 0x004cfb4a, 0x0046f54b, 0x01440041, 0xf249033e, 0x043eff44, 0xf34b0b37, - 0x05400c46, 0x0f060613, 0x07100c0e, 0x120d0d0b, 0x0d0f0f10, 0x0c170d17, - 0x0f140e1a, 0x0e2c1128, 0x112f1811, 0x15151916, 0x1f1b161d, 0x13230e32, - 0x0a39073f, 0xfe4dfc52, 0xfd5e0945, 0xf46d24dd, 0x24de20e6, 0x25e22ce0, - 0x22ee22f1, 0x28f121f9, 0x23fb2100, 0x2602210d, 0x17230d3a, 0x1dfd1a00, - 0x161e1ff9, 0x23f122fd, 0x220324ff, 0x2205200b, 0x2305220c, 0x270b1e1d, - 0x221a1d27, 0x13421f15, 0x1f1f1932, 0xef78ec70, 0xee72f555, 0xf15cf259, - 0xe647f151, 0xf2500044, 0xf246e838, 0xe944e832, 0xf54a17f3, 0x1af328f1, - 0x31f22c03, 0x2d062c22, 0x21361352, 0xfd4bff17, 0x0122012b, 0x0036fe37, - 0x003d0140, 0x0044f75c, 0xf26af361, 0xf15af45a, 0xee58f649, 0xf74ff256, - 0xf649f646, 0xf645fb42, 0xf740fb3a, 0x023b15f6, 0x18f51cf8, 0x1cff1d03, - 0x1d092314, 0x1d240e43, 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, - 0xfa35ff36, 0x07331721, 0x17021500, 0x01090031, 0xdb760539, 0xf34ef541, - 0x013e0c31, 0xfc491132, 0x1240092b, 0x1d001a43, 0x105a0968, 0xd27fec68, - 0x0143f34e, 0xf541013e, 0xfa56ef5f, 0xfa3d092d, 0xfd45fa51, 0xf5600637, - 0x0743fb56, 0x0258003a, 0xfd4cf65e, 0x05360445, 0xfd510058, 0xf943fb4a, - 0xfc4afb50, 0xf948013a, 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, - 0x0d29033e, 0x002dfc4e, 0xfd60e57e, 0xe462e765, 0xe943e452, 0xec5ef053, - 0xea6eeb5b, 0xee66f35d, 0xe37ff95c, 0xfb59f960, 0xf36cfd2e, 0xff41ff39, - 0xf75dfd4a, 0xf75cf857, 0xe97e0536, 0x063c063b, 0x0645ff30, 0x0044fc45, - 0xf858fe55, 0xfa4eff4b, 0xf94d0236, 0x0532fd44, 0x0132062a, 0xfc51013f, - 0xfc460043, 0x0239fe4c, 0x0b230440, 0x013d0b23, 0x12190c18, 0x0d1d0d24, - 0xf65df949, 0xfe490d2e, 0x0931f964, 0x09350235, 0x0535fe3d, 0x00380038, - 0xf33ffb3c, 0xff3e0439, 0xfa450439, 0x0e270433, 0x0d440340, 0x013d093f, - 0x07321027, 0x052c0434, 0x0b30fb3c, 0xff3b003b, 0x1621052c, 0x0e2bff4e, - 0x003c0945, 0x0b1c0228, 0x032c0031, 0x002e022c, 0x0233002f, 0x0427023e, - 0x062e0036, 0x0336023a, 0x043f0633, 0x06390735, 0x06340637, 0x0b2d0e24, - 0x0835ff52, 0x0737fd4e, 0x0f2e161f, 0xff541907, 0x1ef91c03, 0x1c042000, - 0x22ff1e06, 0x1e062009, 0x1f131a1b, 0x1a1e2514, 0x1c221146, 0x0143053b, - 0x0943101e, 0x12201223, 0x161d181f, 0x1726122b, 0x14290b3f, 0x093b0940, - 0xff5efe59, 0xf76cfa4c, 0xfe2c002d, 0x0034fd40, 0xfe3bfc46, 0xfc4bf852, - 0xef66f74d, 0x0318002a, 0x00300037, 0xfa3bf947, 0xf453f557, 0xe277013a, - 0xfd1dff24, 0x0126022b, 0xfa37003a, 0x0040fd4a, 0xf65a0046, 0xfc1d051f, - 0x072a013b, 0xfe3afd48, 0xfd51f561, 0x003a0805, 0x0a0e0e12, 0x0d1b0228, - 0x003afd46, 0xfa4ff855, 0x0000f36a, 0xf06af657, 0xeb72ee6e, 0xf262ea6e, - 0xeb6aee67, 0xeb6be96c, 0xe670f660, 0xf45ffb5b, 0xf75dea5e, 0xfb560943, - 0xfc50f655, 0xff46073c, 0x093a053d, 0x0c320f32, 0x12311136, 0x0a29072e, - 0xff330731, 0x08340929, 0x062f0237, 0x0d290a2c, 0x06320535, 0x0d31043f, - 0x0640fe45, 0xfe3b0646, 0x0a2c091f, 0x0c2b0335, 0x0e220a26, 0xfd340d28, - 0x1120072c, 0x07260d32, 0x0a391a2b, 0x0e0b0b0e, 0x090b120b, 0x150917fe, - 0x20f120f1, 0x22eb27e9, 0x2adf29e1, 0x2ee426f4, 0x151d2de8, 0x35d330e6, - 0x41d52bed, 0x27f61e09, 0x121a141b, 0x0039f252, 0xfb4bed61, 0xdd7d1b00, - 0x1c001ffc, 0x1b062208, 0x1e0a1816, 0x21131620, 0x1a1f1529, 0x1a2c172f, - 0x10410e47, 0x083c063f, 0x11411518, 0x17141a17, 0x1b201c17, 0x1c181728, - 0x18201c1d, 0x172a1339, 0x1635163d, 0x0b560c28, 0x0b330e3b, 0xfc4ff947, - 0xfb45f746, 0xf842f644, 0xed49f445, 0xf046f143, 0xec3eed46, 0xf042ea41, - 0xec3f09fe, 0x1af721f7, 0x27f929fe, 0x2d033109, 0x2d1b243b, 0xfa42f923, - 0xf92af82d, 0xfb30f438, 0xfa3cfb3e, 0xf842f84c, 0xfb55fa51, 0xf64df951, - 0xef50ee49, 0xfc4af653, 0xf747f743, 0xff3df842, 0xf242003b, 0x023b15f3, - 0x21f227f9, 0x2efe3302, 0x3c063d11, 0x37222a3e, 0x14f10236, 0x034a14f1, - 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331619, 0x22001000, 0xfe090429, - 0xe3760241, 0xfa47f34f, 0x05340932, 0xfd460a36, 0x1a221316, 0x28003902, - 0x29241a45, 0xd37ff165, 0xfc4cfa47, 0xf34f0534, 0x0645f35a, 0x0034082b, - 0xfe45fb52, 0xf660023b, 0x024bfd57, 0xfd640138, 0xfd4afa55, 0x003bfd51, - 0xf956fb5f, 0xff42ff4d, 0x0146fe56, 0xfb48003d, 0x0029003f, 0x003f003f, - 0xf7530456, 0x0061f948, 0x0d29033e, 0x0d0f0733, 0x0250d97f, 0xee5bef60, - 0xe651dd62, 0xe866e961, 0xe577e863, 0xeb6eee66, 0xdc7f0050, 0xfb59f95e, - 0xfc5c0027, 0x0041f154, 0xdd7ffe49, 0xf468f75b, 0xe17f0337, 0x07380737, - 0x083dfd35, 0x0044f94a, 0xf758f367, 0xf35bf759, 0xf25cf84c, 0xf457e96e, - 0xe869f64e, 0xec70ef63, 0xb27fba7f, 0xce7fd27f, 0xfc42fb4e, 0xfc47f848, - 0x023bff37, 0xf946fa4b, 0xf859de77, 0xfd4b2014, 0x1e16d47f, 0x0036fb3d, - 0x003aff3c, 0xfd3df843, 0xe754f24a, 0xfb410534, 0x0239003d, 0xf745f546, - 0x1237fc47, 0x003a073d, 0x09291219, 0x0920052b, 0x092f002c, 0x0033022e, - 0x1326fc42, 0x0f260c2a, 0x09220059, 0x042d0a1c, 0x0a1f21f5, 0x34d5120f, - 0x1c0023ea, 0x26e72200, 0x27ee20f4, 0x66a20000, 0x38f121fc, 0x1d0a25fb, - 0x33e327f7, 0x34de45c6, 0x43c12cfb, 0x200737e3, 0x20010000, 0x1b2421e7, - 0x22e224e4, 0x26e426e5, 0x22ee23f0, 0x22f220f8, 0x25fa2300, 0x1e0a1c12, - 0x1a191d29, 0x004b0248, 0x084d0e23, 0x121f1123, 0x151e112d, 0x142a122d, - 0x1b1a1036, 0x07421038, 0x0b490a43, 0xf674e970, 0xf147f93d, 0x0035fb42, - 0xf54df750, 0xf754f657, 0xde7feb65, 0xfd27fb35, 0xf93df54b, 0xf14def5b, - 0xe76be76f, 0xe47af54c, 0xf62cf634, 0xf639f73a, 0xf048f945, 0xfc45fb4a, - 0xf7560242, 0xf7220120, 0x0b1f0534, 0xfe37fe43, 0x0049f859, 0x03340704, - 0x0a081108, 0x10130325, 0xff3dfb49, 0xff46fc4e, 0x0000eb7e, 0xe97cec6e, - 0xe67ee77c, 0xef69e579, 0xe575ef66, 0xe675e574, 0xdf7af65f, 0xf264f85f, - 0xef6fe472, 0xfa59fe50, 0xfc52f755, 0xf851ff48, 0x05400143, 0x09380045, - 0x01450745, 0xf945fa43, 0xf04dfe40, 0x023dfa43, 0xfd400239, 0xfd41fd42, - 0x003e0933, 0xff42fe47, 0xfe4bff46, 0xf7480e3c, 0x1025002f, 0x12230b25, - 0x0c290a29, 0x02300c29, 0x0d29003b, 0x03321328, 0x03421232, 0x13fa12fa, - 0x0e001af4, 0x1ff021e7, 0x21ea25e4, 0x27e22ae2, 0x2fd62ddc, 0x31de29ef, - 0x200945b9, 0x3fc142c0, 0x4db636d9, 0x34dd29f6, 0x240028ff, 0x1e0e1c1a, - 0x17250c37, 0x0b4125df, 0x27dc28db, 0x26e22edf, 0x2ae228e8, 0x31e326f4, - 0x28f626fd, 0x2efb1f14, 0x1d1e192c, 0x0c300b31, 0x1a2d1616, 0x17161b15, - 0x21141a1c, 0x1e181b22, 0x122a1927, 0x12320c46, 0x15360e47, 0x0b531920, - 0x15311536, 0xfb55fa51, 0xf64df951, 0xef50ee49, 0xfc4af653, 0xf747f743, - 0xff3df842, 0xf242003b, 0x023b11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, - 0x421b2f39, 0xfb470018, 0xff24fe2a, 0xfe34f739, 0xfa3ffc41, 0xfc43f952, - 0xfd51fd4c, 0xf948fa4e, 0xf448f244, 0xfd46fa4c, 0xfb42fb3e, 0x0039fc3d, - 0xf73c0136, 0x023a11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, 0x421b2f39, - 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331d10, - 0x19000e00, 0xf633fd3e, 0xe5631a10, 0xfc55e866, 0x05390639, 0xef490e39, - 0x1428140a, 0x1d003600, 0x252a0c61, 0xe07fea75, 0xfe4afc55, 0xe8660539, - 0xfa5df258, 0xfa2c0437, 0xf559f167, 0xeb741339, 0x143a0454, 0x0660013f, - 0xfb55f36a, 0x053f064b, 0xfd5aff65, 0x0337fc4f, 0xfe4bf461, 0xf932013c, - 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x0722f758, - 0xec7fdc7f, 0xef5bf25f, 0xe754e756, 0xf459ef5b, 0xe17ff24c, 0xee67f35a, - 0xdb7f0b50, 0x054c0254, 0x054efa37, 0x043df253, 0xdb7ffb4f, 0xf568f55b, - 0xe27f0041, 0xfe4f0048, 0xfc5cfa38, 0x0344f847, 0xf362fc56, 0xf458fb52, - 0xfd48fc43, 0xf848f059, 0xf745ff3b, 0x05420439, 0xfc47fe47, 0x023aff4a, - 0xfc2cff45, 0x003ef933, 0xfc2ffa2a, 0xfd29fa35, 0x084cf74e, 0xf5530934, - 0x0043fb5a, 0x0143f148, 0xfb4bf850, 0xeb53eb40, 0xf31fe740, 0xe35e094b, - 0x113ff84a, 0xfb23fe1b, 0x0d5b0341, 0xf945084d, 0xf642033e, 0xfd44ec51, - 0x001e0107, 0xfd17eb4a, 0x1042e97c, 0x11252cee, 0x32deea7f, 0x0427002a, - 0x07220b1d, 0x081f0625, 0x072a0328, 0x08210d2b, 0x0d24042f, 0x0337023a, - 0x063c082c, 0x0b2c0e2a, 0x07300438, 0x04340d25, 0x0931133a, 0x0a300c2d, - 0x00451421, 0x083f23ee, 0x21e71cfd, 0x180a1b00, 0x22f234d4, 0x27e81311, - 0x1f19241d, 0x1821220f, 0x1e141649, 0x1422131f, 0x1b2c1310, 0x0f240f24, - 0x151c1915, 0x1e141f0c, 0x1b10182a, 0x005d0e38, 0x0f391a26, 0xe87fe873, - 0xea52f73e, 0x0035003b, 0xf255f359, 0xf35ef55c, 0xe37feb64, 0xf239f443, - 0xf547f64d, 0xeb55f058, 0xe968f162, 0xdb7ff652, 0xf830f83d, 0xf842f946, - 0xf24bf64f, 0xf753f45c, 0xee6cfc4f, 0xea45f04b, 0xfe3a013a, 0xf34ef753, - 0xfc51f363, 0xf351fa26, 0xf33efa3a, 0xfe3bf049, 0xf64cf356, 0xf753f657, - 0x0000ea7f, 0xe77fe778, 0xe57fed72, 0xe975e776, 0xe675e871, 0xe476e178, - 0xdb7cf65e, 0xf166f663, 0xf36ace7f, 0xfb5c1139, 0xfb56f35e, 0xf45bfe4d, - 0x0047ff49, 0x0440f951, 0x05400f39, 0x01430044, 0xf6430144, 0x004d0240, - 0x0044fb4e, 0x0737053b, 0x02410e36, 0x0f2c053c, 0x0246fe4c, 0xee560c46, - 0x0540f446, 0x0b370538, 0x00450241, 0xfa4a0536, 0x0736fa4c, 0xf552fe4d, - 0xfe4d192a, 0x11f310f7, 0x11f41beb, 0x25e229d8, 0x2ad730d1, 0x27e02ed8, - 0x34cd2ed7, 0x34d92bed, 0x200b3dc9, 0x38d23ece, 0x51bd2dec, 0x23fe1c0f, - 0x22012701, 0x1e111426, 0x122d0f36, 0x004f24f0, 0x25f225ef, 0x2001220f, - 0x1d0f1819, 0x22161f10, 0x23121f1c, 0x2129241c, 0x1b2f153e, 0x121f131a, - 0x24181817, 0x1b10181e, 0x1f1d1629, 0x162a103c, 0x0f340e3c, 0x034ef07b, - 0x15351638, 0x193d1521, 0x1332113d, 0xfd4ef84a, 0xf748f648, 0xee4bf447, - 0xf53ffb46, 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, - 0x21ff2107, 0x1f0c2517, 0x1f261440, 0xf747f925, 0xf82cf531, 0xf638f43b, - 0xf83ff743, 0xfa44f64f, 0xfd4ef84a, 0xf748f648, 0xee4bf447, 0xf53ffb46, - 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, 0x21ff2107, - 0x1f0c2517, 0x1f261440 -}; - -static void -assemble_scaling_list(struct hantro_ctx *ctx) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - const size_t num_list_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4); - const size_t list_len_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4[0]); - const size_t list_len_8x8 = ARRAY_SIZE(scaling->scaling_list_8x8[0]); - struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; - u32 *dst = (u32 *)tbl->scaling_list; - const u32 *src; - int i, j; - - if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) - return; - - for (i = 0; i < num_list_4x4; i++) { - src = (u32 *)&scaling->scaling_list_4x4[i]; - for (j = 0; j < list_len_4x4 / 4; j++) - *dst++ = swab32(src[j]); - } - - /* Only Intra/Inter Y lists */ - for (i = 0; i < 2; i++) { - src = (u32 *)&scaling->scaling_list_8x8[i]; - for (j = 0; j < list_len_8x8 / 4; j++) - *dst++ = swab32(src[j]); - } -} - -static void prepare_table(struct hantro_ctx *ctx) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; - const struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; - u32 dpb_longterm = 0; - u32 dpb_valid = 0; - int i; - - for (i = 0; i < HANTRO_H264_DPB_SIZE; ++i) { - tbl->poc[i * 2] = dpb[i].top_field_order_cnt; - tbl->poc[i * 2 + 1] = dpb[i].bottom_field_order_cnt; - - if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) - continue; - - /* - * Set up bit maps of valid and long term DPBs. - * NOTE: The bits are reversed, i.e. MSb is DPB 0. For frame - * decoding, bit 31 to 15 are used, while for field decoding, - * all bits are used, with bit 31 being a top field, 30 a bottom - * field and so on. - */ - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) { - if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) - dpb_valid |= REF_BIT(i * 2); - - if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) - dpb_valid |= REF_BIT(i * 2 + 1); - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) { - dpb_longterm |= REF_BIT(i * 2); - dpb_longterm |= REF_BIT(i * 2 + 1); - } - } else { - dpb_valid |= REF_BIT(i); - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - dpb_longterm |= REF_BIT(i); - } - } - ctx->h264_dec.dpb_valid = dpb_valid; - ctx->h264_dec.dpb_longterm = dpb_longterm; - - if ((dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) || - !(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)) { - tbl->poc[32] = ctx->h264_dec.cur_poc; - tbl->poc[33] = 0; - } else { - tbl->poc[32] = dec_param->top_field_order_cnt; - tbl->poc[33] = dec_param->bottom_field_order_cnt; - } - - assemble_scaling_list(ctx); -} - -static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, - const struct v4l2_h264_dpb_entry *b) -{ - return a->reference_ts == b->reference_ts; -} - -static void update_dpb(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_h264_decode_params *dec_param; - DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - DECLARE_BITMAP(used, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - unsigned int i, j; - - dec_param = ctx->h264_dec.ctrls.decode; - - /* Disable all entries by default. */ - for (i = 0; i < ARRAY_SIZE(ctx->h264_dec.dpb); i++) - ctx->h264_dec.dpb[i].flags = 0; - - /* Try to match new DPB entries with existing ones by their POCs. */ - for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - - if (!(ndpb->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) - continue; - - /* - * To cut off some comparisons, iterate only on target DPB - * entries which are not used yet. - */ - for_each_clear_bit(j, used, ARRAY_SIZE(ctx->h264_dec.dpb)) { - struct v4l2_h264_dpb_entry *cdpb; - - cdpb = &ctx->h264_dec.dpb[j]; - if (!dpb_entry_match(cdpb, ndpb)) - continue; - - *cdpb = *ndpb; - set_bit(j, used); - break; - } - - if (j == ARRAY_SIZE(ctx->h264_dec.dpb)) - set_bit(i, new); - } - - /* For entries that could not be matched, use remaining free slots. */ - for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - struct v4l2_h264_dpb_entry *cdpb; - - /* - * Both arrays are of the same sizes, so there is no way - * we can end up with no space in target array, unless - * something is buggy. - */ - j = find_first_zero_bit(used, ARRAY_SIZE(ctx->h264_dec.dpb)); - if (WARN_ON(j >= ARRAY_SIZE(ctx->h264_dec.dpb))) - return; - - cdpb = &ctx->h264_dec.dpb[j]; - *cdpb = *ndpb; - set_bit(j, used); - } -} - -dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, - unsigned int dpb_idx) -{ - struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; - dma_addr_t dma_addr = 0; - s32 cur_poc = ctx->h264_dec.cur_poc; - u32 flags; - - if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - dma_addr = hantro_get_ref(ctx, dpb[dpb_idx].reference_ts); - - if (!dma_addr) { - struct vb2_v4l2_buffer *dst_buf; - struct vb2_buffer *buf; - - /* - * If a DPB entry is unused or invalid, address of current - * destination buffer is returned. - */ - dst_buf = hantro_get_dst_buf(ctx); - buf = &dst_buf->vb2_buf; - dma_addr = hantro_get_dec_buf_addr(ctx, buf); - } - - flags = dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD ? 0x2 : 0; - flags |= abs(dpb[dpb_idx].top_field_order_cnt - cur_poc) < - abs(dpb[dpb_idx].bottom_field_order_cnt - cur_poc) ? - 0x1 : 0; - - return dma_addr | flags; -} - -u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, unsigned int dpb_idx) -{ - const struct v4l2_h264_dpb_entry *dpb = &ctx->h264_dec.dpb[dpb_idx]; - - if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - return 0; - return dpb->frame_num; -} - -/* - * Removes all references with the same parity as the current picture from the - * reference list. The remaining list will have references with the opposite - * parity. This is effectively a deduplication of references since each buffer - * stores two fields. For this reason, each buffer is found twice in the - * reference list. - * - * This technique has been chosen through trial and error. This simple approach - * resulted in the highest conformance score. Note that this method may suffer - * worse quality in the case an opposite reference frame has been lost. If this - * becomes a problem in the future, it should be possible to add a preprocessing - * to identify un-paired fields and avoid removing them. - */ -static void deduplicate_reflist(struct v4l2_h264_reflist_builder *b, - struct v4l2_h264_reference *reflist) -{ - int write_idx = 0; - int i; - - if (b->cur_pic_fields == V4L2_H264_FRAME_REF) { - write_idx = b->num_valid; - goto done; - } - - for (i = 0; i < b->num_valid; i++) { - if (!(b->cur_pic_fields == reflist[i].fields)) { - reflist[write_idx++] = reflist[i]; - continue; - } - } - -done: - /* Should not happen unless we have a bug in the reflist builder. */ - if (WARN_ON(write_idx > 16)) - write_idx = 16; - - /* Clear the remaining, some streams fails otherwise */ - for (; write_idx < 16; write_idx++) - reflist[write_idx].index = 15; -} - -int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx) -{ - struct hantro_h264_dec_hw_ctx *h264_ctx = &ctx->h264_dec; - struct hantro_h264_dec_ctrls *ctrls = &h264_ctx->ctrls; - struct v4l2_h264_reflist_builder reflist_builder; - - hantro_start_prepare_run(ctx); - - ctrls->scaling = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); - if (WARN_ON(!ctrls->scaling)) - return -EINVAL; - - ctrls->decode = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); - if (WARN_ON(!ctrls->decode)) - return -EINVAL; - - ctrls->sps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SPS); - if (WARN_ON(!ctrls->sps)) - return -EINVAL; - - ctrls->pps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_PPS); - if (WARN_ON(!ctrls->pps)) - return -EINVAL; - - /* Update the DPB with new refs. */ - update_dpb(ctx); - - /* Build the P/B{0,1} ref lists. */ - v4l2_h264_init_reflist_builder(&reflist_builder, ctrls->decode, - ctrls->sps, ctx->h264_dec.dpb); - h264_ctx->cur_poc = reflist_builder.cur_pic_order_count; - - /* Prepare data in memory. */ - prepare_table(ctx); - - v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); - v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, - h264_ctx->reflists.b1); - - /* - * Reduce ref lists to at most 16 entries, Hantro hardware will deduce - * the actual picture lists in field through the dpb_valid, - * dpb_longterm bitmap along with the current frame parity. - */ - if (reflist_builder.cur_pic_fields != V4L2_H264_FRAME_REF) { - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.p); - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b0); - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b1); - } - - return 0; -} - -void hantro_h264_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; - struct hantro_aux_buf *priv = &h264_dec->priv; - - dma_free_coherent(vpu->dev, priv->size, priv->cpu, priv->dma); -} - -int hantro_h264_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; - struct hantro_aux_buf *priv = &h264_dec->priv; - struct hantro_h264_dec_priv_tbl *tbl; - - priv->cpu = dma_alloc_coherent(vpu->dev, sizeof(*tbl), &priv->dma, - GFP_KERNEL); - if (!priv->cpu) - return -ENOMEM; - - priv->size = sizeof(*tbl); - tbl = priv->cpu; - memcpy(tbl->cabac_table, h264_cabac_table, sizeof(tbl->cabac_table)); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_hevc.c b/drivers/staging/media/hantro/hantro_hevc.c deleted file mode 100644 index b990bc98164c..000000000000 --- a/drivers/staging/media/hantro/hantro_hevc.c +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU HEVC codec driver - * - * Copyright (C) 2020 Safran Passenger Innovations LLC - */ - -#include -#include - -#include "hantro.h" -#include "hantro_hw.h" - -#define VERT_FILTER_RAM_SIZE 8 /* bytes per pixel row */ -/* - * BSD control data of current picture at tile border - * 128 bits per 4x4 tile = 128/(8*4) bytes per row - */ -#define BSD_CTRL_RAM_SIZE 4 /* bytes per pixel row */ -/* tile border coefficients of filter */ -#define VERT_SAO_RAM_SIZE 48 /* bytes per pixel */ - -#define SCALING_LIST_SIZE (16 * 64) - -#define MAX_TILE_COLS 20 -#define MAX_TILE_ROWS 22 - -void hantro_hevc_ref_init(struct hantro_ctx *ctx) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - - hevc_dec->ref_bufs_used = 0; -} - -dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, - s32 poc) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - int i; - - /* Find the reference buffer in already known ones */ - for (i = 0; i < NUM_REF_PICTURES; i++) { - if (hevc_dec->ref_bufs_poc[i] == poc) { - hevc_dec->ref_bufs_used |= 1 << i; - return hevc_dec->ref_bufs[i].dma; - } - } - - return 0; -} - -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - int i; - - /* Add a new reference buffer */ - for (i = 0; i < NUM_REF_PICTURES; i++) { - if (!(hevc_dec->ref_bufs_used & 1 << i)) { - hevc_dec->ref_bufs_used |= 1 << i; - hevc_dec->ref_bufs_poc[i] = poc; - hevc_dec->ref_bufs[i].dma = addr; - return 0; - } - } - - return -EINVAL; -} - -static int tile_buffer_reallocate(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; - unsigned int height64 = (sps->pic_height_in_luma_samples + 63) & ~63; - unsigned int size; - - if (num_tile_cols <= 1 || - num_tile_cols <= hevc_dec->num_tile_cols_allocated) - return 0; - - /* Need to reallocate due to tiles passed via PPS */ - if (hevc_dec->tile_filter.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - } - - if (hevc_dec->tile_sao.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - } - - if (hevc_dec->tile_bsd.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; - } - - size = VERT_FILTER_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_filter.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_filter.dma, - GFP_KERNEL); - if (!hevc_dec->tile_filter.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_filter.size = size; - - size = VERT_SAO_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_sao.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_sao.dma, - GFP_KERNEL); - if (!hevc_dec->tile_sao.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_sao.size = size; - - size = BSD_CTRL_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_bsd.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_bsd.dma, - GFP_KERNEL); - if (!hevc_dec->tile_bsd.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_bsd.size = size; - - hevc_dec->num_tile_cols_allocated = num_tile_cols; - - return 0; - -err_free_tile_buffers: - if (hevc_dec->tile_filter.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - - if (hevc_dec->tile_sao.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - - if (hevc_dec->tile_bsd.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; - - return -ENOMEM; -} - -static int hantro_hevc_validate_sps(struct hantro_ctx *ctx, const struct v4l2_ctrl_hevc_sps *sps) -{ - /* - * for tile pixel format check if the width and height match - * hardware constraints - */ - if (ctx->vpu_dst_fmt->fourcc == V4L2_PIX_FMT_NV12_4L4) { - if (ctx->dst_fmt.width != - ALIGN(sps->pic_width_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_width)) - return -EINVAL; - - if (ctx->dst_fmt.height != - ALIGN(sps->pic_height_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_height)) - return -EINVAL; - } - - return 0; -} - -int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx) -{ - struct hantro_hevc_dec_hw_ctx *hevc_ctx = &ctx->hevc_dec; - struct hantro_hevc_dec_ctrls *ctrls = &hevc_ctx->ctrls; - int ret; - - hantro_start_prepare_run(ctx); - - ctrls->decode_params = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); - if (WARN_ON(!ctrls->decode_params)) - return -EINVAL; - - ctrls->scaling = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); - if (WARN_ON(!ctrls->scaling)) - return -EINVAL; - - ctrls->sps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SPS); - if (WARN_ON(!ctrls->sps)) - return -EINVAL; - - ret = hantro_hevc_validate_sps(ctx, ctrls->sps); - if (ret) - return ret; - - ctrls->pps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_PPS); - if (WARN_ON(!ctrls->pps)) - return -EINVAL; - - ret = tile_buffer_reallocate(ctx); - if (ret) - return ret; - - return 0; -} - -void hantro_hevc_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - - if (hevc_dec->tile_sizes.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sizes.size, - hevc_dec->tile_sizes.cpu, - hevc_dec->tile_sizes.dma); - hevc_dec->tile_sizes.cpu = NULL; - - if (hevc_dec->scaling_lists.cpu) - dma_free_coherent(vpu->dev, hevc_dec->scaling_lists.size, - hevc_dec->scaling_lists.cpu, - hevc_dec->scaling_lists.dma); - hevc_dec->scaling_lists.cpu = NULL; - - if (hevc_dec->tile_filter.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - - if (hevc_dec->tile_sao.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - - if (hevc_dec->tile_bsd.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; -} - -int hantro_hevc_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - unsigned int size; - - memset(hevc_dec, 0, sizeof(*hevc_dec)); - - /* - * Maximum number of tiles times width and height (2 bytes each), - * rounding up to next 16 bytes boundary + one extra 16 byte - * chunk (HW guys wanted to have this). - */ - size = round_up(MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 16, 16); - hevc_dec->tile_sizes.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_sizes.dma, - GFP_KERNEL); - if (!hevc_dec->tile_sizes.cpu) - return -ENOMEM; - - hevc_dec->tile_sizes.size = size; - - hevc_dec->scaling_lists.cpu = dma_alloc_coherent(vpu->dev, SCALING_LIST_SIZE, - &hevc_dec->scaling_lists.dma, - GFP_KERNEL); - if (!hevc_dec->scaling_lists.cpu) - return -ENOMEM; - - hevc_dec->scaling_lists.size = SCALING_LIST_SIZE; - - hantro_hevc_ref_init(ctx); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h deleted file mode 100644 index e83f0c523a30..000000000000 --- a/drivers/staging/media/hantro/hantro_hw.h +++ /dev/null @@ -1,441 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa - */ - -#ifndef HANTRO_HW_H_ -#define HANTRO_HW_H_ - -#include -#include -#include -#include -#include - -#define DEC_8190_ALIGN_MASK 0x07U - -#define MB_DIM 16 -#define TILE_MB_DIM 4 -#define MB_WIDTH(w) DIV_ROUND_UP(w, MB_DIM) -#define MB_HEIGHT(h) DIV_ROUND_UP(h, MB_DIM) - -#define FMT_MIN_WIDTH 48 -#define FMT_MIN_HEIGHT 48 -#define FMT_HD_WIDTH 1280 -#define FMT_HD_HEIGHT 720 -#define FMT_FHD_WIDTH 1920 -#define FMT_FHD_HEIGHT 1088 -#define FMT_UHD_WIDTH 3840 -#define FMT_UHD_HEIGHT 2160 -#define FMT_4K_WIDTH 4096 -#define FMT_4K_HEIGHT 2304 - -#define NUM_REF_PICTURES (V4L2_HEVC_DPB_ENTRIES_NUM_MAX + 1) - -struct hantro_dev; -struct hantro_ctx; -struct hantro_buf; -struct hantro_variant; - -/** - * struct hantro_aux_buf - auxiliary DMA buffer for hardware data - * - * @cpu: CPU pointer to the buffer. - * @dma: DMA address of the buffer. - * @size: Size of the buffer. - * @attrs: Attributes of the DMA mapping. - */ -struct hantro_aux_buf { - void *cpu; - dma_addr_t dma; - size_t size; - unsigned long attrs; -}; - -/* Max. number of entries in the DPB (HW limitation). */ -#define HANTRO_H264_DPB_SIZE 16 - -/** - * struct hantro_h264_dec_ctrls - * - * @decode: Decode params - * @scaling: Scaling info - * @sps: SPS info - * @pps: PPS info - */ -struct hantro_h264_dec_ctrls { - const struct v4l2_ctrl_h264_decode_params *decode; - const struct v4l2_ctrl_h264_scaling_matrix *scaling; - const struct v4l2_ctrl_h264_sps *sps; - const struct v4l2_ctrl_h264_pps *pps; -}; - -/** - * struct hantro_h264_dec_reflists - * - * @p: P reflist - * @b0: B0 reflist - * @b1: B1 reflist - */ -struct hantro_h264_dec_reflists { - struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; -}; - -/** - * struct hantro_h264_dec_hw_ctx - * - * @priv: Private auxiliary buffer for hardware. - * @dpb: DPB - * @reflists: P/B0/B1 reflists - * @ctrls: V4L2 controls attached to a run - * @dpb_longterm: DPB long-term - * @dpb_valid: DPB valid - * @cur_poc: Current picture order count - */ -struct hantro_h264_dec_hw_ctx { - struct hantro_aux_buf priv; - struct v4l2_h264_dpb_entry dpb[HANTRO_H264_DPB_SIZE]; - struct hantro_h264_dec_reflists reflists; - struct hantro_h264_dec_ctrls ctrls; - u32 dpb_longterm; - u32 dpb_valid; - s32 cur_poc; -}; - -/** - * struct hantro_hevc_dec_ctrls - * @decode_params: Decode params - * @scaling: Scaling matrix - * @sps: SPS info - * @pps: PPS info - * @hevc_hdr_skip_length: the number of data (in bits) to skip in the - * slice segment header syntax after 'slice type' - * token - */ -struct hantro_hevc_dec_ctrls { - const struct v4l2_ctrl_hevc_decode_params *decode_params; - const struct v4l2_ctrl_hevc_scaling_matrix *scaling; - const struct v4l2_ctrl_hevc_sps *sps; - const struct v4l2_ctrl_hevc_pps *pps; - u32 hevc_hdr_skip_length; -}; - -/** - * struct hantro_hevc_dec_hw_ctx - * @tile_sizes: Tile sizes buffer - * @tile_filter: Tile vertical filter buffer - * @tile_sao: Tile SAO buffer - * @tile_bsd: Tile BSD control buffer - * @ref_bufs: Internal reference buffers - * @scaling_lists: Scaling lists buffer - * @ref_bufs_poc: Internal reference buffers picture order count - * @ref_bufs_used: Bitfield of used reference buffers - * @ctrls: V4L2 controls attached to a run - * @num_tile_cols_allocated: number of allocated tiles - */ -struct hantro_hevc_dec_hw_ctx { - struct hantro_aux_buf tile_sizes; - struct hantro_aux_buf tile_filter; - struct hantro_aux_buf tile_sao; - struct hantro_aux_buf tile_bsd; - struct hantro_aux_buf ref_bufs[NUM_REF_PICTURES]; - struct hantro_aux_buf scaling_lists; - s32 ref_bufs_poc[NUM_REF_PICTURES]; - u32 ref_bufs_used; - struct hantro_hevc_dec_ctrls ctrls; - unsigned int num_tile_cols_allocated; -}; - -/** - * struct hantro_mpeg2_dec_hw_ctx - * - * @qtable: Quantization table - */ -struct hantro_mpeg2_dec_hw_ctx { - struct hantro_aux_buf qtable; -}; - -/** - * struct hantro_vp8_dec_hw_ctx - * - * @segment_map: Segment map buffer. - * @prob_tbl: Probability table buffer. - */ -struct hantro_vp8_dec_hw_ctx { - struct hantro_aux_buf segment_map; - struct hantro_aux_buf prob_tbl; -}; - -/** - * struct hantro_vp9_frame_info - * - * @valid: frame info valid flag - * @frame_context_idx: index of frame context - * @reference_mode: inter prediction type - * @tx_mode: transform mode - * @interpolation_filter: filter selection for inter prediction - * @flags: frame flags - * @timestamp: frame timestamp - */ -struct hantro_vp9_frame_info { - u32 valid : 1; - u32 frame_context_idx : 2; - u32 reference_mode : 2; - u32 tx_mode : 3; - u32 interpolation_filter : 3; - u32 flags; - u64 timestamp; -}; - -#define MAX_SB_COLS 64 -#define MAX_SB_ROWS 34 - -/** - * struct hantro_vp9_dec_hw_ctx - * - * @tile_edge: auxiliary DMA buffer for tile edge processing - * @segment_map: auxiliary DMA buffer for segment map - * @misc: auxiliary DMA buffer for tile info, probabilities and hw counters - * @cnts: vp9 library struct for abstracting hw counters access - * @probability_tables: VP9 probability tables implied by the spec - * @frame_context: VP9 frame contexts - * @cur: current frame information - * @last: last frame information - * @bsd_ctrl_offset: bsd offset into tile_edge - * @segment_map_size: size of segment map - * @ctx_counters_offset: hw counters offset into misc - * @tile_info_offset: tile info offset into misc - * @tile_r_info: per-tile information array - * @tile_c_info: per-tile information array - * @last_tile_r: last number of tile rows - * @last_tile_c: last number of tile cols - * @last_sbs_r: last number of superblock rows - * @last_sbs_c: last number of superblock cols - * @active_segment: number of active segment (alternating between 0 and 1) - * @feature_enabled: segmentation feature enabled flags - * @feature_data: segmentation feature data - */ -struct hantro_vp9_dec_hw_ctx { - struct hantro_aux_buf tile_edge; - struct hantro_aux_buf segment_map; - struct hantro_aux_buf misc; - struct v4l2_vp9_frame_symbol_counts cnts; - struct v4l2_vp9_frame_context probability_tables; - struct v4l2_vp9_frame_context frame_context[4]; - struct hantro_vp9_frame_info cur; - struct hantro_vp9_frame_info last; - - unsigned int bsd_ctrl_offset; - unsigned int segment_map_size; - unsigned int ctx_counters_offset; - unsigned int tile_info_offset; - - unsigned short tile_r_info[MAX_SB_ROWS]; - unsigned short tile_c_info[MAX_SB_COLS]; - unsigned int last_tile_r; - unsigned int last_tile_c; - unsigned int last_sbs_r; - unsigned int last_sbs_c; - - unsigned int active_segment; - u8 feature_enabled[8]; - s16 feature_data[8][4]; -}; - -/** - * struct hantro_postproc_ctx - * - * @dec_q: References buffers, in decoder format. - */ -struct hantro_postproc_ctx { - struct hantro_aux_buf dec_q[VB2_MAX_FRAME]; -}; - -/** - * struct hantro_postproc_ops - post-processor operations - * - * @enable: Enable the post-processor block. Optional. - * @disable: Disable the post-processor block. Optional. - * @enum_framesizes: Enumerate possible scaled output formats. - * Returns zero if OK, a negative value in error cases. - * Optional. - */ -struct hantro_postproc_ops { - void (*enable)(struct hantro_ctx *ctx); - void (*disable)(struct hantro_ctx *ctx); - int (*enum_framesizes)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize); -}; - -/** - * struct hantro_codec_ops - codec mode specific operations - * - * @init: If needed, can be used for initialization. - * Optional and called from process context. - * @exit: If needed, can be used to undo the .init phase. - * Optional and called from process context. - * @run: Start single {en,de)coding job. Called from atomic context - * to indicate that a pair of buffers is ready and the hardware - * should be programmed and started. Returns zero if OK, a - * negative value in error cases. - * @done: Read back processing results and additional data from hardware. - * @reset: Reset the hardware in case of a timeout. - */ -struct hantro_codec_ops { - int (*init)(struct hantro_ctx *ctx); - void (*exit)(struct hantro_ctx *ctx); - int (*run)(struct hantro_ctx *ctx); - void (*done)(struct hantro_ctx *ctx); - void (*reset)(struct hantro_ctx *ctx); -}; - -/** - * enum hantro_enc_fmt - source format ID for hardware registers. - * - * @ROCKCHIP_VPU_ENC_FMT_YUV420P: Y/CbCr 4:2:0 planar format - * @ROCKCHIP_VPU_ENC_FMT_YUV420SP: Y/CbCr 4:2:0 semi-planar format - * @ROCKCHIP_VPU_ENC_FMT_YUYV422: YUV 4:2:2 packed format (YUYV) - * @ROCKCHIP_VPU_ENC_FMT_UYVY422: YUV 4:2:2 packed format (UYVY) - */ -enum hantro_enc_fmt { - ROCKCHIP_VPU_ENC_FMT_YUV420P = 0, - ROCKCHIP_VPU_ENC_FMT_YUV420SP = 1, - ROCKCHIP_VPU_ENC_FMT_YUYV422 = 2, - ROCKCHIP_VPU_ENC_FMT_UYVY422 = 3, -}; - -extern const struct hantro_variant imx8mm_vpu_g1_variant; -extern const struct hantro_variant imx8mq_vpu_g1_variant; -extern const struct hantro_variant imx8mq_vpu_g2_variant; -extern const struct hantro_variant imx8mq_vpu_variant; -extern const struct hantro_variant px30_vpu_variant; -extern const struct hantro_variant rk3036_vpu_variant; -extern const struct hantro_variant rk3066_vpu_variant; -extern const struct hantro_variant rk3288_vpu_variant; -extern const struct hantro_variant rk3328_vpu_variant; -extern const struct hantro_variant rk3399_vpu_variant; -extern const struct hantro_variant rk3568_vepu_variant; -extern const struct hantro_variant rk3568_vpu_variant; -extern const struct hantro_variant sama5d4_vdec_variant; -extern const struct hantro_variant sunxi_vpu_variant; - -extern const struct hantro_postproc_ops hantro_g1_postproc_ops; -extern const struct hantro_postproc_ops hantro_g2_postproc_ops; - -extern const u32 hantro_vp8_dec_mc_filter[8][6]; - -void hantro_watchdog(struct work_struct *work); -void hantro_run(struct hantro_ctx *ctx); -void hantro_irq_done(struct hantro_dev *vpu, - enum vb2_buffer_state result); -void hantro_start_prepare_run(struct hantro_ctx *ctx); -void hantro_end_prepare_run(struct hantro_ctx *ctx); - -irqreturn_t hantro_g1_irq(int irq, void *dev_id); -void hantro_g1_reset(struct hantro_ctx *ctx); - -int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); -int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx); -void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx); -void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx); - -dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, - unsigned int dpb_idx); -u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, - unsigned int dpb_idx); -int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx); -int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx); -int hantro_g1_h264_dec_run(struct hantro_ctx *ctx); -int hantro_h264_dec_init(struct hantro_ctx *ctx); -void hantro_h264_dec_exit(struct hantro_ctx *ctx); - -int hantro_hevc_dec_init(struct hantro_ctx *ctx); -void hantro_hevc_dec_exit(struct hantro_ctx *ctx); -int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx); -int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx); -void hantro_hevc_ref_init(struct hantro_ctx *ctx); -dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, s32 poc); -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr); - - -static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension) -{ - return (dimension + 63) / 64; -} - -static inline size_t -hantro_vp9_mv_size(unsigned int width, unsigned int height) -{ - int num_ctbs; - - /* - * There can be up to (CTBs x 64) number of blocks, - * and the motion vector for each block needs 16 bytes. - */ - num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height); - return (num_ctbs * 64) * 16; -} - -static inline size_t -hantro_h264_mv_size(unsigned int width, unsigned int height) -{ - /* - * A decoded 8-bit 4:2:0 NV12 frame may need memory for up to - * 448 bytes per macroblock with additional 32 bytes on - * multi-core variants. - * - * The H264 decoder needs extra space on the output buffers - * to store motion vectors. This is needed for reference - * frames and only if the format is non-post-processed NV12. - * - * Memory layout is as follow: - * - * +---------------------------+ - * | Y-plane 256 bytes x MBs | - * +---------------------------+ - * | UV-plane 128 bytes x MBs | - * +---------------------------+ - * | MV buffer 64 bytes x MBs | - * +---------------------------+ - * | MC sync 32 bytes | - * +---------------------------+ - */ - return 64 * MB_WIDTH(width) * MB_WIDTH(height) + 32; -} - -static inline size_t -hantro_hevc_mv_size(unsigned int width, unsigned int height) -{ - /* - * A CTB can be 64x64, 32x32 or 16x16. - * Allocated memory for the "worse" case: 16x16 - */ - return width * height / 16; -} - -int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); -int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); -void hantro_mpeg2_dec_copy_qtable(u8 *qtable, - const struct v4l2_ctrl_mpeg2_quantisation *ctrl); -int hantro_mpeg2_dec_init(struct hantro_ctx *ctx); -void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx); - -int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx); -int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx); -int hantro_vp8_dec_init(struct hantro_ctx *ctx); -void hantro_vp8_dec_exit(struct hantro_ctx *ctx); -void hantro_vp8_prob_update(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr); - -int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx); -void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx); -int hantro_vp9_dec_init(struct hantro_ctx *ctx); -void hantro_vp9_dec_exit(struct hantro_ctx *ctx); -void hantro_g2_check_idle(struct hantro_dev *vpu); -irqreturn_t hantro_g2_irq(int irq, void *dev_id); - -#endif /* HANTRO_HW_H_ */ diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c deleted file mode 100644 index d07b1b449b61..000000000000 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ /dev/null @@ -1,348 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) Collabora, Ltd. - * - * Based on GSPCA and CODA drivers: - * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include -#include -#include -#include -#include "hantro_jpeg.h" -#include "hantro.h" - -#define LUMA_QUANT_OFF 25 -#define CHROMA_QUANT_OFF 90 -#define HEIGHT_OFF 159 -#define WIDTH_OFF 161 - -#define HUFF_LUMA_DC_OFF 178 -#define HUFF_LUMA_AC_OFF 211 -#define HUFF_CHROMA_DC_OFF 394 -#define HUFF_CHROMA_AC_OFF 427 - -/* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 - */ -static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, - 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, - 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, - 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, - 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, - 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, - 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 -}; - -static const unsigned char chroma_q_table[] = { - 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, - 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, - 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const unsigned char zigzag[] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - -static const u32 hw_reorder[] = { - 0, 8, 16, 24, 1, 9, 17, 25, - 32, 40, 48, 56, 33, 41, 49, 57, - 2, 10, 18, 26, 3, 11, 19, 27, - 34, 42, 50, 58, 35, 43, 51, 59, - 4, 12, 20, 28, 5, 13, 21, 29, - 36, 44, 52, 60, 37, 45, 53, 61, - 6, 14, 22, 30, 7, 15, 23, 31, - 38, 46, 54, 62, 39, 47, 55, 63 -}; - -/* Huffman tables are shared with CODA */ -static const unsigned char luma_dc_table[] = { - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -static const unsigned char chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -/* For simplicity, we keep a pre-formatted JPEG header, - * and we'll use fixed offsets to change the width, height - * quantization tables, etc. - */ -static const unsigned char hantro_jpeg_header[] = { - /* SOI */ - 0xff, 0xd8, - - /* JFIF-APP0 */ - 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x00, - - /* DQT */ - 0xff, 0xdb, 0x00, 0x84, - - 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* SOF */ - 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, - 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, - 0x03, 0x11, 0x01, - - /* DHT */ - 0xff, 0xc4, 0x00, 0x1f, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0xb5, 0x10, - - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0x1f, 0x01, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0xb5, 0x11, - - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* COM */ - 0xff, 0xfe, 0x00, 0x03, 0x00, - - /* SOS */ - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, - 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, -}; - -/* - * JPEG_HEADER_SIZE is used in other parts of the driver in lieu of - * "sizeof(hantro_jpeg_header)". The two must be equal. - */ -static_assert(sizeof(hantro_jpeg_header) == JPEG_HEADER_SIZE); - -/* - * hantro_jpeg_header is padded with a COM segment, so that the payload - * of the SOS segment (the entropy-encoded image scan), which should - * trail the whole header, is 8-byte aligned for the hardware to write - * to directly. - */ -static_assert(IS_ALIGNED(sizeof(hantro_jpeg_header), 8), - "Hantro JPEG header size needs to be 8-byte aligned."); - -static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) -{ - unsigned int temp; - - temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - - return (unsigned char)temp; -} - -static void -jpeg_scale_quant_table(unsigned char *file_q_tab, - unsigned char *reordered_q_tab, - const unsigned char *tab, int scale) -{ - int i; - - BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); - - for (i = 0; i < JPEG_QUANT_SIZE; i++) { - file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); - reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); - } -} - -static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) -{ - int scale; - - /* - * Non-linear scaling factor: - * [5,50] -> [1000..100], [51,100] -> [98..0] - */ - if (ctx->quality < 50) - scale = 5000 / ctx->quality; - else - scale = 200 - 2 * ctx->quality; - - BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); - - jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, - ctx->hw_luma_qtable, luma_q_table, scale); - jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, - ctx->hw_chroma_qtable, chroma_q_table, scale); -} - -void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) -{ - char *buf = ctx->buffer; - - memcpy(buf, hantro_jpeg_header, - sizeof(hantro_jpeg_header)); - - buf[HEIGHT_OFF + 0] = ctx->height >> 8; - buf[HEIGHT_OFF + 1] = ctx->height; - buf[WIDTH_OFF + 0] = ctx->width >> 8; - buf[WIDTH_OFF + 1] = ctx->width; - - memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); - memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); - memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, - sizeof(chroma_dc_table)); - memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, - sizeof(chroma_ac_table)); - - jpeg_set_quality(ctx); -} diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h deleted file mode 100644 index 0b49d0b82caa..000000000000 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ - -#define JPEG_HEADER_SIZE 624 -#define JPEG_QUANT_SIZE 64 - -struct hantro_jpeg_ctx { - int width; - int height; - int quality; - unsigned char *buffer; - unsigned char hw_luma_qtable[JPEG_QUANT_SIZE]; - unsigned char hw_chroma_qtable[JPEG_QUANT_SIZE]; -}; - -void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/staging/media/hantro/hantro_mpeg2.c b/drivers/staging/media/hantro/hantro_mpeg2.c deleted file mode 100644 index 04e545eb0a83..000000000000 --- a/drivers/staging/media/hantro/hantro_mpeg2.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include "hantro.h" - -static const u8 zigzag[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - -void hantro_mpeg2_dec_copy_qtable(u8 *qtable, - const struct v4l2_ctrl_mpeg2_quantisation *ctrl) -{ - int i, n; - - if (!qtable || !ctrl) - return; - - for (i = 0; i < ARRAY_SIZE(zigzag); i++) { - n = zigzag[i]; - qtable[n + 0] = ctrl->intra_quantiser_matrix[i]; - qtable[n + 64] = ctrl->non_intra_quantiser_matrix[i]; - qtable[n + 128] = ctrl->chroma_intra_quantiser_matrix[i]; - qtable[n + 192] = ctrl->chroma_non_intra_quantiser_matrix[i]; - } -} - -int hantro_mpeg2_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - ctx->mpeg2_dec.qtable.size = ARRAY_SIZE(zigzag) * 4; - ctx->mpeg2_dec.qtable.cpu = - dma_alloc_coherent(vpu->dev, - ctx->mpeg2_dec.qtable.size, - &ctx->mpeg2_dec.qtable.dma, - GFP_KERNEL); - if (!ctx->mpeg2_dec.qtable.cpu) - return -ENOMEM; - return 0; -} - -void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - dma_free_coherent(vpu->dev, - ctx->mpeg2_dec.qtable.size, - ctx->mpeg2_dec.qtable.cpu, - ctx->mpeg2_dec.qtable.dma); -} diff --git a/drivers/staging/media/hantro/hantro_postproc.c b/drivers/staging/media/hantro/hantro_postproc.c deleted file mode 100644 index a0928c508434..000000000000 --- a/drivers/staging/media/hantro/hantro_postproc.c +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro G1 post-processor support - * - * Copyright (C) 2019 Collabora, Ltd. - */ - -#include -#include - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_g1_regs.h" -#include "hantro_g2_regs.h" -#include "hantro_v4l2.h" - -#define HANTRO_PP_REG_WRITE(vpu, reg_name, val) \ -{ \ - hantro_reg_write(vpu, \ - &hantro_g1_postproc_regs.reg_name, \ - val); \ -} - -#define HANTRO_PP_REG_WRITE_S(vpu, reg_name, val) \ -{ \ - hantro_reg_write_s(vpu, \ - &hantro_g1_postproc_regs.reg_name, \ - val); \ -} - -#define VPU_PP_IN_YUYV 0x0 -#define VPU_PP_IN_NV12 0x1 -#define VPU_PP_IN_YUV420 0x2 -#define VPU_PP_IN_YUV240_TILED 0x5 -#define VPU_PP_OUT_RGB 0x0 -#define VPU_PP_OUT_YUYV 0x3 - -static const struct hantro_postproc_regs hantro_g1_postproc_regs = { - .pipeline_en = {G1_REG_PP_INTERRUPT, 1, 0x1}, - .max_burst = {G1_REG_PP_DEV_CONFIG, 0, 0x1f}, - .clk_gate = {G1_REG_PP_DEV_CONFIG, 1, 0x1}, - .out_swap32 = {G1_REG_PP_DEV_CONFIG, 5, 0x1}, - .out_endian = {G1_REG_PP_DEV_CONFIG, 6, 0x1}, - .out_luma_base = {G1_REG_PP_OUT_LUMA_BASE, 0, 0xffffffff}, - .input_width = {G1_REG_PP_INPUT_SIZE, 0, 0x1ff}, - .input_height = {G1_REG_PP_INPUT_SIZE, 9, 0x1ff}, - .output_width = {G1_REG_PP_CONTROL, 4, 0x7ff}, - .output_height = {G1_REG_PP_CONTROL, 15, 0x7ff}, - .input_fmt = {G1_REG_PP_CONTROL, 29, 0x7}, - .output_fmt = {G1_REG_PP_CONTROL, 26, 0x7}, - .orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff}, - .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff}, -}; - -bool hantro_needs_postproc(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt) -{ - if (ctx->is_encoder) - return false; - return fmt->postprocessed; -} - -static void hantro_postproc_g1_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *dst_buf; - u32 src_pp_fmt, dst_pp_fmt; - dma_addr_t dst_dma; - - /* Turn on pipeline mode. Must be done first. */ - HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x1); - - src_pp_fmt = VPU_PP_IN_NV12; - - switch (ctx->vpu_dst_fmt->fourcc) { - case V4L2_PIX_FMT_YUYV: - dst_pp_fmt = VPU_PP_OUT_YUYV; - break; - default: - WARN(1, "output format %d not supported by the post-processor, this wasn't expected.", - ctx->vpu_dst_fmt->fourcc); - dst_pp_fmt = 0; - break; - } - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - - HANTRO_PP_REG_WRITE(vpu, clk_gate, 0x1); - HANTRO_PP_REG_WRITE(vpu, out_endian, 0x1); - HANTRO_PP_REG_WRITE(vpu, out_swap32, 0x1); - HANTRO_PP_REG_WRITE(vpu, max_burst, 16); - HANTRO_PP_REG_WRITE(vpu, out_luma_base, dst_dma); - HANTRO_PP_REG_WRITE(vpu, input_width, MB_WIDTH(ctx->dst_fmt.width)); - HANTRO_PP_REG_WRITE(vpu, input_height, MB_HEIGHT(ctx->dst_fmt.height)); - HANTRO_PP_REG_WRITE(vpu, input_fmt, src_pp_fmt); - HANTRO_PP_REG_WRITE(vpu, output_fmt, dst_pp_fmt); - HANTRO_PP_REG_WRITE(vpu, output_width, ctx->dst_fmt.width); - HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height); - HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width)); - HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width); -} - -static int down_scale_factor(struct hantro_ctx *ctx) -{ - if (ctx->src_fmt.width == ctx->dst_fmt.width) - return 0; - - return DIV_ROUND_CLOSEST(ctx->src_fmt.width, ctx->dst_fmt.width); -} - -static void hantro_postproc_g2_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *dst_buf; - int down_scale = down_scale_factor(ctx); - size_t chroma_offset; - dma_addr_t dst_dma; - - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - chroma_offset = ctx->dst_fmt.plane_fmt[0].bytesperline * - ctx->dst_fmt.height; - - if (down_scale) { - hantro_reg_write(vpu, &g2_down_scale_e, 1); - hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2); - hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2); - hantro_write_addr(vpu, G2_DS_DST, dst_dma); - hantro_write_addr(vpu, G2_DS_DST_CHR, dst_dma + (chroma_offset >> down_scale)); - } else { - hantro_write_addr(vpu, G2_RS_OUT_LUMA_ADDR, dst_dma); - hantro_write_addr(vpu, G2_RS_OUT_CHROMA_ADDR, dst_dma + chroma_offset); - } - if (ctx->dev->variant->legacy_regs) { - int out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat); - u8 pp_shift = 0; - - if (out_depth > 8) - pp_shift = 16 - out_depth; - - hantro_reg_write(ctx->dev, &g2_rs_out_bit_depth, out_depth); - hantro_reg_write(ctx->dev, &g2_pp_pix_shift, pp_shift); - } - hantro_reg_write(vpu, &g2_out_rs_e, 1); -} - -static int hantro_postproc_g2_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize) -{ - /** - * G2 scaler can scale down by 0, 2, 4 or 8 - * use fsize->index has power of 2 diviser - **/ - if (fsize->index > 3) - return -EINVAL; - - if (!ctx->src_fmt.width || !ctx->src_fmt.height) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = ctx->src_fmt.width >> fsize->index; - fsize->discrete.height = ctx->src_fmt.height >> fsize->index; - - return 0; -} - -void hantro_postproc_free(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - for (i = 0; i < VB2_MAX_FRAME; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; - - if (priv->cpu) { - dma_free_attrs(vpu->dev, priv->size, priv->cpu, - priv->dma, priv->attrs); - priv->cpu = NULL; - } - } -} - -int hantro_postproc_alloc(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; - unsigned int num_buffers = cap_queue->num_buffers; - struct v4l2_pix_format_mplane pix_mp; - const struct hantro_fmt *fmt; - unsigned int i, buf_size; - - /* this should always pick native format */ - fmt = hantro_get_default_fmt(ctx, false); - if (!fmt) - return -EINVAL; - v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, - ctx->src_fmt.height); - - buf_size = pix_mp.plane_fmt[0].sizeimage; - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) - buf_size += hantro_h264_mv_size(pix_mp.width, - pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) - buf_size += hantro_vp9_mv_size(pix_mp.width, - pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) - buf_size += hantro_hevc_mv_size(pix_mp.width, - pix_mp.height); - - for (i = 0; i < num_buffers; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; - - /* - * The buffers on this queue are meant as intermediate - * buffers for the decoder, so no mapping is needed. - */ - priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; - priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, - GFP_KERNEL, priv->attrs); - if (!priv->cpu) - return -ENOMEM; - priv->size = buf_size; - } - return 0; -} - -static void hantro_postproc_g1_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x0); -} - -static void hantro_postproc_g2_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - hantro_reg_write(vpu, &g2_out_rs_e, 0); -} - -void hantro_postproc_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->disable) - vpu->variant->postproc_ops->disable(ctx); -} - -void hantro_postproc_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enable) - vpu->variant->postproc_ops->enable(ctx); -} - -int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enum_framesizes) - return vpu->variant->postproc_ops->enum_framesizes(ctx, fsize); - - return -EINVAL; -} - -const struct hantro_postproc_ops hantro_g1_postproc_ops = { - .enable = hantro_postproc_g1_enable, - .disable = hantro_postproc_g1_disable, -}; - -const struct hantro_postproc_ops hantro_g2_postproc_ops = { - .enable = hantro_postproc_g2_enable, - .disable = hantro_postproc_g2_disable, - .enum_framesizes = hantro_postproc_g2_enum_framesizes, -}; diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c deleted file mode 100644 index 2c7a805289e7..000000000000 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ /dev/null @@ -1,990 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Collabora, Ltd. - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin - * Jeffy Chen - * - * Copyright 2018 Google LLC. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -static int hantro_set_fmt_out(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp); -static int hantro_set_fmt_cap(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp); - -static const struct hantro_fmt * -hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) -{ - const struct hantro_fmt *formats; - - if (ctx->is_encoder) { - formats = ctx->dev->variant->enc_fmts; - *num_fmts = ctx->dev->variant->num_enc_fmts; - } else { - formats = ctx->dev->variant->dec_fmts; - *num_fmts = ctx->dev->variant->num_dec_fmts; - } - - return formats; -} - -static const struct hantro_fmt * -hantro_get_postproc_formats(const struct hantro_ctx *ctx, - unsigned int *num_fmts) -{ - struct hantro_dev *vpu = ctx->dev; - - if (ctx->is_encoder || !vpu->variant->postproc_fmts) { - *num_fmts = 0; - return NULL; - } - - *num_fmts = ctx->dev->variant->num_postproc_fmts; - return ctx->dev->variant->postproc_fmts; -} - -int hantro_get_format_depth(u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_P010: - case V4L2_PIX_FMT_P010_4L4: - return 10; - default: - return 8; - } -} - -static bool -hantro_check_depth_match(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt) -{ - int fmt_depth, ctx_depth = 8; - - if (!fmt->match_depth && !fmt->postprocessed) - return true; - - /* 0 means default depth, which is 8 */ - if (ctx->bit_depth) - ctx_depth = ctx->bit_depth; - - fmt_depth = hantro_get_format_depth(fmt->fourcc); - - /* - * Allow only downconversion for postproc formats for now. - * It may be possible to relax that on some HW. - */ - if (!fmt->match_depth) - return fmt_depth <= ctx_depth; - - return fmt_depth == ctx_depth; -} - -static const struct hantro_fmt * -hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) -{ - const struct hantro_fmt *formats; - unsigned int i, num_fmts; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) - if (formats[i].fourcc == fourcc) - return &formats[i]; - - formats = hantro_get_postproc_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) - if (formats[i].fourcc == fourcc) - return &formats[i]; - return NULL; -} - -const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream) -{ - const struct hantro_fmt *formats; - unsigned int i, num_fmts; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - if (bitstream == (formats[i].codec_mode != - HANTRO_MODE_NONE) && - hantro_check_depth_match(ctx, &formats[i])) - return &formats[i]; - } - return NULL; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct hantro_dev *vpu = video_drvdata(file); - struct video_device *vdev = video_devdata(file); - - strscpy(cap->driver, vpu->dev->driver->name, sizeof(cap->driver)); - strscpy(cap->card, vdev->name, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform: %s", - vpu->dev->driver->name); - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - const struct hantro_fmt *fmt; - - fmt = hantro_find_format(ctx, fsize->pixel_format); - if (!fmt) { - vpu_debug(0, "unsupported bitstream format (%08x)\n", - fsize->pixel_format); - return -EINVAL; - } - - /* For non-coded formats check if postprocessing scaling is possible */ - if (fmt->codec_mode == HANTRO_MODE_NONE && hantro_needs_postproc(ctx, fmt)) { - return hanto_postproc_enum_framesizes(ctx, fsize); - } else if (fsize->index != 0) { - vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", - fsize->index); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = fmt->frmsize; - - return 0; -} - -static int vidioc_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f, bool capture) - -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - const struct hantro_fmt *fmt, *formats; - unsigned int num_fmts, i, j = 0; - bool skip_mode_none; - - /* - * When dealing with an encoder: - * - on the capture side we want to filter out all MODE_NONE formats. - * - on the output side we want to filter out all formats that are - * not MODE_NONE. - * When dealing with a decoder: - * - on the capture side we want to filter out all formats that are - * not MODE_NONE. - * - on the output side we want to filter out all MODE_NONE formats. - */ - skip_mode_none = capture == ctx->is_encoder; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; - fmt = &formats[i]; - - if (skip_mode_none == mode_none) - continue; - if (!hantro_check_depth_match(ctx, fmt)) - continue; - if (j == f->index) { - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - - /* - * Enumerate post-processed formats. As per the specification, - * we enumerated these formats after natively decoded formats - * as a hint for applications on what's the preferred fomat. - */ - if (!capture) - return -EINVAL; - formats = hantro_get_postproc_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - fmt = &formats[i]; - - if (!hantro_check_depth_match(ctx, fmt)) - continue; - if (j == f->index) { - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, priv, f, true); -} - -static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, priv, f, false); -} - -static int vidioc_g_fmt_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct hantro_ctx *ctx = fh_to_ctx(priv); - - vpu_debug(4, "f->type = %d\n", f->type); - - *pix_mp = ctx->src_fmt; - - return 0; -} - -static int vidioc_g_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct hantro_ctx *ctx = fh_to_ctx(priv); - - vpu_debug(4, "f->type = %d\n", f->type); - - *pix_mp = ctx->dst_fmt; - - return 0; -} - -static int hantro_try_fmt(const struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp, - enum v4l2_buf_type type) -{ - const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = V4L2_TYPE_IS_CAPTURE(type); - bool coded; - - coded = capture == ctx->is_encoder; - - vpu_debug(4, "trying format %c%c%c%c\n", - (pix_mp->pixelformat & 0x7f), - (pix_mp->pixelformat >> 8) & 0x7f, - (pix_mp->pixelformat >> 16) & 0x7f, - (pix_mp->pixelformat >> 24) & 0x7f); - - fmt = hantro_find_format(ctx, pix_mp->pixelformat); - if (!fmt) { - fmt = hantro_get_default_fmt(ctx, coded); - pix_mp->pixelformat = fmt->fourcc; - } - - if (coded) { - pix_mp->num_planes = 1; - vpu_fmt = fmt; - } else if (ctx->is_encoder) { - vpu_fmt = ctx->vpu_dst_fmt; - } else { - vpu_fmt = fmt; - /* - * Width/height on the CAPTURE end of a decoder are ignored and - * replaced by the OUTPUT ones. - */ - pix_mp->width = ctx->src_fmt.width; - pix_mp->height = ctx->src_fmt.height; - } - - pix_mp->field = V4L2_FIELD_NONE; - - v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, - &vpu_fmt->frmsize); - - if (!coded) { - /* Fill remaining fields */ - v4l2_fill_pixfmt_mp(pix_mp, fmt->fourcc, pix_mp->width, - pix_mp->height); - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_h264_mv_size(pix_mp->width, - pix_mp->height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_vp9_mv_size(pix_mp->width, - pix_mp->height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_hevc_mv_size(pix_mp->width, - pix_mp->height); - } else if (!pix_mp->plane_fmt[0].sizeimage) { - /* - * For coded formats the application can specify - * sizeimage. If the application passes a zero sizeimage, - * let's default to the maximum frame size. - */ - pix_mp->plane_fmt[0].sizeimage = fmt->header_size + - pix_mp->width * pix_mp->height * fmt->max_depth; - } - - return 0; -} - -static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); -} - -static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); -} - -static void -hantro_reset_fmt(struct v4l2_pix_format_mplane *fmt, - const struct hantro_fmt *vpu_fmt) -{ - memset(fmt, 0, sizeof(*fmt)); - - fmt->pixelformat = vpu_fmt->fourcc; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_JPEG; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - fmt->quantization = V4L2_QUANTIZATION_DEFAULT; - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -} - -static void -hantro_reset_encoded_fmt(struct hantro_ctx *ctx) -{ - const struct hantro_fmt *vpu_fmt; - struct v4l2_pix_format_mplane *fmt; - - vpu_fmt = hantro_get_default_fmt(ctx, true); - - if (ctx->is_encoder) { - ctx->vpu_dst_fmt = vpu_fmt; - fmt = &ctx->dst_fmt; - } else { - ctx->vpu_src_fmt = vpu_fmt; - fmt = &ctx->src_fmt; - } - - hantro_reset_fmt(fmt, vpu_fmt); - fmt->width = vpu_fmt->frmsize.min_width; - fmt->height = vpu_fmt->frmsize.min_height; - if (ctx->is_encoder) - hantro_set_fmt_cap(ctx, fmt); - else - hantro_set_fmt_out(ctx, fmt); -} - -static void -hantro_reset_raw_fmt(struct hantro_ctx *ctx) -{ - const struct hantro_fmt *raw_vpu_fmt; - struct v4l2_pix_format_mplane *raw_fmt, *encoded_fmt; - - raw_vpu_fmt = hantro_get_default_fmt(ctx, false); - - if (ctx->is_encoder) { - ctx->vpu_src_fmt = raw_vpu_fmt; - raw_fmt = &ctx->src_fmt; - encoded_fmt = &ctx->dst_fmt; - } else { - ctx->vpu_dst_fmt = raw_vpu_fmt; - raw_fmt = &ctx->dst_fmt; - encoded_fmt = &ctx->src_fmt; - } - - hantro_reset_fmt(raw_fmt, raw_vpu_fmt); - raw_fmt->width = encoded_fmt->width; - raw_fmt->height = encoded_fmt->height; - if (ctx->is_encoder) - hantro_set_fmt_out(ctx, raw_fmt); - else - hantro_set_fmt_cap(ctx, raw_fmt); -} - -void hantro_reset_fmts(struct hantro_ctx *ctx) -{ - hantro_reset_encoded_fmt(ctx); - hantro_reset_raw_fmt(ctx); -} - -static void -hantro_update_requires_request(struct hantro_ctx *ctx, u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_JPEG: - ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = false; - break; - case V4L2_PIX_FMT_MPEG2_SLICE: - case V4L2_PIX_FMT_VP8_FRAME: - case V4L2_PIX_FMT_H264_SLICE: - case V4L2_PIX_FMT_HEVC_SLICE: - case V4L2_PIX_FMT_VP9_FRAME: - ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true; - break; - default: - break; - } -} - -static void -hantro_update_requires_hold_capture_buf(struct hantro_ctx *ctx, u32 fourcc) -{ - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - - switch (fourcc) { - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MPEG2_SLICE: - case V4L2_PIX_FMT_VP8_FRAME: - case V4L2_PIX_FMT_HEVC_SLICE: - case V4L2_PIX_FMT_VP9_FRAME: - vq->subsystem_flags &= ~(VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); - break; - case V4L2_PIX_FMT_H264_SLICE: - vq->subsystem_flags |= VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; - break; - default: - break; - } -} - -static int hantro_set_fmt_out(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp) -{ - struct vb2_queue *vq; - int ret; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (ret) - return ret; - - if (!ctx->is_encoder) { - struct vb2_queue *peer_vq; - - /* - * In order to support dynamic resolution change, - * the decoder admits a resolution change, as long - * as the pixelformat remains. Can't be done if streaming. - */ - if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && - pix_mp->pixelformat != ctx->src_fmt.pixelformat)) - return -EBUSY; - /* - * Since format change on the OUTPUT queue will reset - * the CAPTURE queue, we can't allow doing so - * when the CAPTURE queue has buffers allocated. - */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(peer_vq)) - return -EBUSY; - } else { - /* - * The encoder doesn't admit a format change if - * there are OUTPUT buffers allocated. - */ - if (vb2_is_busy(vq)) - return -EBUSY; - } - - ctx->vpu_src_fmt = hantro_find_format(ctx, pix_mp->pixelformat); - ctx->src_fmt = *pix_mp; - - /* - * Current raw format might have become invalid with newly - * selected codec, so reset it to default just to be safe and - * keep internal driver state sane. User is mandated to set - * the raw format again after we return, so we don't need - * anything smarter. - * Note that hantro_reset_raw_fmt() also propagates size - * changes to the raw format. - */ - if (!ctx->is_encoder) - hantro_reset_raw_fmt(ctx); - - /* Colorimetry information are always propagated. */ - ctx->dst_fmt.colorspace = pix_mp->colorspace; - ctx->dst_fmt.ycbcr_enc = pix_mp->ycbcr_enc; - ctx->dst_fmt.xfer_func = pix_mp->xfer_func; - ctx->dst_fmt.quantization = pix_mp->quantization; - - hantro_update_requires_request(ctx, pix_mp->pixelformat); - hantro_update_requires_hold_capture_buf(ctx, pix_mp->pixelformat); - - vpu_debug(0, "OUTPUT codec mode: %d\n", ctx->vpu_src_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d\n", - pix_mp->width, pix_mp->height); - return 0; -} - -static int hantro_set_fmt_cap(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp) -{ - struct vb2_queue *vq; - int ret; - - /* Change not allowed if queue is busy. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - - if (ctx->is_encoder) { - struct vb2_queue *peer_vq; - - /* - * Since format change on the CAPTURE queue will reset - * the OUTPUT queue, we can't allow doing so - * when the OUTPUT queue has buffers allocated. - */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (vb2_is_busy(peer_vq) && - (pix_mp->pixelformat != ctx->dst_fmt.pixelformat || - pix_mp->height != ctx->dst_fmt.height || - pix_mp->width != ctx->dst_fmt.width)) - return -EBUSY; - } - - ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (ret) - return ret; - - ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat); - ctx->dst_fmt = *pix_mp; - - /* - * Current raw format might have become invalid with newly - * selected codec, so reset it to default just to be safe and - * keep internal driver state sane. User is mandated to set - * the raw format again after we return, so we don't need - * anything smarter. - * Note that hantro_reset_raw_fmt() also propagates size - * changes to the raw format. - */ - if (ctx->is_encoder) - hantro_reset_raw_fmt(ctx); - - /* Colorimetry information are always propagated. */ - ctx->src_fmt.colorspace = pix_mp->colorspace; - ctx->src_fmt.ycbcr_enc = pix_mp->ycbcr_enc; - ctx->src_fmt.xfer_func = pix_mp->xfer_func; - ctx->src_fmt.quantization = pix_mp->quantization; - - vpu_debug(0, "CAPTURE codec mode: %d\n", ctx->vpu_dst_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d\n", - pix_mp->width, pix_mp->height); - - hantro_update_requires_request(ctx, pix_mp->pixelformat); - - return 0; -} - -static int -vidioc_s_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - return hantro_set_fmt_out(fh_to_ctx(priv), &f->fmt.pix_mp); -} - -static int -vidioc_s_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - return hantro_set_fmt_cap(fh_to_ctx(priv), &f->fmt.pix_mp); -} - -static int vidioc_g_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - - /* Crop only supported on source. */ - if (!ctx->is_encoder || - sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = ctx->src_fmt.width; - sel->r.height = ctx->src_fmt.height; - break; - case V4L2_SEL_TGT_CROP: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = ctx->dst_fmt.width; - sel->r.height = ctx->dst_fmt.height; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - struct v4l2_rect *rect = &sel->r; - struct vb2_queue *vq; - - /* Crop only supported on source. */ - if (!ctx->is_encoder || - sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - /* Change not allowed if the queue is streaming. */ - vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx); - if (vb2_is_streaming(vq)) - return -EBUSY; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* - * We do not support offsets, and we can crop only inside - * right-most or bottom-most macroblocks. - */ - if (rect->left != 0 || rect->top != 0 || - round_up(rect->width, MB_DIM) != ctx->src_fmt.width || - round_up(rect->height, MB_DIM) != ctx->src_fmt.height) { - /* Default to full frame for incorrect settings. */ - rect->left = 0; - rect->top = 0; - rect->width = ctx->src_fmt.width; - rect->height = ctx->src_fmt.height; - } else { - /* We support widths aligned to 4 pixels and arbitrary heights. */ - rect->width = round_up(rect->width, 4); - } - - ctx->dst_fmt.width = rect->width; - ctx->dst_fmt.height = rect->height; - - return 0; -} - -static const struct v4l2_event hantro_eos_event = { - .type = V4L2_EVENT_EOS -}; - -static int vidioc_encoder_cmd(struct file *file, void *priv, - struct v4l2_encoder_cmd *ec) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, ec); - if (ret < 0) - return ret; - - if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || - !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) - return 0; - - ret = v4l2_m2m_ioctl_encoder_cmd(file, priv, ec); - if (ret < 0) - return ret; - - if (ec->cmd == V4L2_ENC_CMD_STOP && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - - if (ec->cmd == V4L2_ENC_CMD_START) - vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - - return 0; -} - -const struct v4l2_ioctl_ops hantro_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_cap_mplane, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_out_mplane, - .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_out_mplane, - .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_cap_mplane, - .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_out_mplane, - .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - - .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 = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = vidioc_encoder_cmd, -}; - -static int -hantro_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_pix_format_mplane *pixfmt; - int i; - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - pixfmt = &ctx->dst_fmt; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - pixfmt = &ctx->src_fmt; - break; - default: - vpu_err("invalid queue type: %d\n", vq->type); - return -EINVAL; - } - - if (*num_planes) { - if (*num_planes != pixfmt->num_planes) - return -EINVAL; - for (i = 0; i < pixfmt->num_planes; ++i) - if (sizes[i] < pixfmt->plane_fmt[i].sizeimage) - return -EINVAL; - return 0; - } - - *num_planes = pixfmt->num_planes; - for (i = 0; i < pixfmt->num_planes; ++i) - sizes[i] = pixfmt->plane_fmt[i].sizeimage; - return 0; -} - -static int -hantro_buf_plane_check(struct vb2_buffer *vb, - struct v4l2_pix_format_mplane *pixfmt) -{ - unsigned int sz; - int i; - - for (i = 0; i < pixfmt->num_planes; ++i) { - sz = pixfmt->plane_fmt[i].sizeimage; - vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", - i, vb2_plane_size(vb, i), sz); - if (vb2_plane_size(vb, i) < sz) { - vpu_err("plane %d is too small for output\n", i); - return -EINVAL; - } - } - return 0; -} - -static int hantro_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct hantro_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_pix_format_mplane *pix_fmt; - int ret; - - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - pix_fmt = &ctx->src_fmt; - else - pix_fmt = &ctx->dst_fmt; - ret = hantro_buf_plane_check(vb, pix_fmt); - if (ret) - return ret; - /* - * Buffer's bytesused must be written by driver for CAPTURE buffers. - * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets - * it to buffer length). - */ - if (V4L2_TYPE_IS_CAPTURE(vq->type)) { - if (ctx->is_encoder) - vb2_set_plane_payload(vb, 0, 0); - else - vb2_set_plane_payload(vb, 0, pix_fmt->plane_fmt[0].sizeimage); - } - - return 0; -} - -static void hantro_buf_queue(struct vb2_buffer *vb) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && - vb2_is_streaming(vb->vb2_queue) && - v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { - unsigned int i; - - for (i = 0; i < vb->num_planes; i++) - vb2_set_plane_payload(vb, i, 0); - - vbuf->field = V4L2_FIELD_NONE; - vbuf->sequence = ctx->sequence_cap++; - - v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - return; - } - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static bool hantro_vq_is_coded(struct vb2_queue *q) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); -} - -static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - int ret = 0; - - v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - ctx->sequence_out = 0; - else - ctx->sequence_cap = 0; - - if (hantro_vq_is_coded(q)) { - enum hantro_codec_mode codec_mode; - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - codec_mode = ctx->vpu_src_fmt->codec_mode; - else - codec_mode = ctx->vpu_dst_fmt->codec_mode; - - vpu_debug(4, "Codec mode = %d\n", codec_mode); - ctx->codec_ops = &ctx->dev->variant->codec_ops[codec_mode]; - if (ctx->codec_ops->init) { - ret = ctx->codec_ops->init(ctx); - if (ret) - return ret; - } - - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) { - ret = hantro_postproc_alloc(ctx); - if (ret) - goto err_codec_exit; - } - } - return ret; - -err_codec_exit: - if (ctx->codec_ops->exit) - ctx->codec_ops->exit(ctx); - return ret; -} - -static void -hantro_return_bufs(struct vb2_queue *q, - struct vb2_v4l2_buffer *(*buf_remove)(struct v4l2_m2m_ctx *)) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - for (;;) { - struct vb2_v4l2_buffer *vbuf; - - vbuf = buf_remove(ctx->fh.m2m_ctx); - if (!vbuf) - break; - v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - } -} - -static void hantro_stop_streaming(struct vb2_queue *q) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - if (hantro_vq_is_coded(q)) { - hantro_postproc_free(ctx); - if (ctx->codec_ops && ctx->codec_ops->exit) - ctx->codec_ops->exit(ctx); - } - - /* - * The mem2mem framework calls v4l2_m2m_cancel_job before - * .stop_streaming, so there isn't any job running and - * it is safe to return all the buffers. - */ - if (V4L2_TYPE_IS_OUTPUT(q->type)) - hantro_return_bufs(q, v4l2_m2m_src_buf_remove); - else - hantro_return_bufs(q, v4l2_m2m_dst_buf_remove); - - v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); - - if (V4L2_TYPE_IS_OUTPUT(q->type) && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); -} - -static void hantro_buf_request_complete(struct vb2_buffer *vb) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler); -} - -static int hantro_buf_out_validate(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - vbuf->field = V4L2_FIELD_NONE; - return 0; -} - -const struct vb2_ops hantro_queue_ops = { - .queue_setup = hantro_queue_setup, - .buf_prepare = hantro_buf_prepare, - .buf_queue = hantro_buf_queue, - .buf_out_validate = hantro_buf_out_validate, - .buf_request_complete = hantro_buf_request_complete, - .start_streaming = hantro_start_streaming, - .stop_streaming = hantro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; diff --git a/drivers/staging/media/hantro/hantro_v4l2.h b/drivers/staging/media/hantro/hantro_v4l2.h deleted file mode 100644 index 64f6f57e9d7a..000000000000 --- a/drivers/staging/media/hantro/hantro_v4l2.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin - * Jeffy Chen - * - * Copyright 2018 Google LLC. - * Tomasz Figa - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#ifndef HANTRO_V4L2_H_ -#define HANTRO_V4L2_H_ - -#include "hantro.h" - -extern const struct v4l2_ioctl_ops hantro_ioctl_ops; -extern const struct vb2_ops hantro_queue_ops; - -void hantro_reset_fmts(struct hantro_ctx *ctx); -int hantro_get_format_depth(u32 fourcc); -const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream); - -#endif /* HANTRO_V4L2_H_ */ diff --git a/drivers/staging/media/hantro/hantro_vp8.c b/drivers/staging/media/hantro/hantro_vp8.c deleted file mode 100644 index 381bc1d3bfda..000000000000 --- a/drivers/staging/media/hantro/hantro_vp8.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include "hantro.h" - -/* - * probs table with packed - */ -struct vp8_prob_tbl_packed { - u8 prob_mb_skip_false; - u8 prob_intra; - u8 prob_ref_last; - u8 prob_ref_golden; - u8 prob_segment[3]; - u8 padding0; - - u8 prob_luma_16x16_pred_mode[4]; - u8 prob_chroma_pred_mode[3]; - u8 padding1; - - /* mv prob */ - u8 prob_mv_context[2][V4L2_VP8_MV_PROB_CNT]; - u8 padding2[2]; - - /* coeff probs */ - u8 prob_coeffs[4][8][3][V4L2_VP8_COEFF_PROB_CNT]; - u8 padding3[96]; -}; - -/* - * filter taps taken to 7-bit precision, - * reference RFC6386#Page-16, filters[8][6] - */ -const u32 hantro_vp8_dec_mc_filter[8][6] = { - { 0, 0, 128, 0, 0, 0 }, - { 0, -6, 123, 12, -1, 0 }, - { 2, -11, 108, 36, -8, 1 }, - { 0, -9, 93, 50, -6, 0 }, - { 3, -16, 77, 77, -16, 3 }, - { 0, -6, 50, 93, -9, 0 }, - { 1, -8, 36, 108, -11, 2 }, - { 0, -1, 12, 123, -6, 0 } -}; - -void hantro_vp8_prob_update(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_entropy *entropy = &hdr->entropy; - u32 i, j, k; - u8 *dst; - - /* first probs */ - dst = ctx->vp8_dec.prob_tbl.cpu; - - dst[0] = hdr->prob_skip_false; - dst[1] = hdr->prob_intra; - dst[2] = hdr->prob_last; - dst[3] = hdr->prob_gf; - dst[4] = hdr->segment.segment_probs[0]; - dst[5] = hdr->segment.segment_probs[1]; - dst[6] = hdr->segment.segment_probs[2]; - dst[7] = 0; - - dst += 8; - dst[0] = entropy->y_mode_probs[0]; - dst[1] = entropy->y_mode_probs[1]; - dst[2] = entropy->y_mode_probs[2]; - dst[3] = entropy->y_mode_probs[3]; - dst[4] = entropy->uv_mode_probs[0]; - dst[5] = entropy->uv_mode_probs[1]; - dst[6] = entropy->uv_mode_probs[2]; - dst[7] = 0; /*unused */ - - /* mv probs */ - dst += 8; - dst[0] = entropy->mv_probs[0][0]; /* is short */ - dst[1] = entropy->mv_probs[1][0]; - dst[2] = entropy->mv_probs[0][1]; /* sign */ - dst[3] = entropy->mv_probs[1][1]; - dst[4] = entropy->mv_probs[0][8 + 9]; - dst[5] = entropy->mv_probs[0][9 + 9]; - dst[6] = entropy->mv_probs[1][8 + 9]; - dst[7] = entropy->mv_probs[1][9 + 9]; - dst += 8; - for (i = 0; i < 2; ++i) { - for (j = 0; j < 8; j += 4) { - dst[0] = entropy->mv_probs[i][j + 9 + 0]; - dst[1] = entropy->mv_probs[i][j + 9 + 1]; - dst[2] = entropy->mv_probs[i][j + 9 + 2]; - dst[3] = entropy->mv_probs[i][j + 9 + 3]; - dst += 4; - } - } - for (i = 0; i < 2; ++i) { - dst[0] = entropy->mv_probs[i][0 + 2]; - dst[1] = entropy->mv_probs[i][1 + 2]; - dst[2] = entropy->mv_probs[i][2 + 2]; - dst[3] = entropy->mv_probs[i][3 + 2]; - dst[4] = entropy->mv_probs[i][4 + 2]; - dst[5] = entropy->mv_probs[i][5 + 2]; - dst[6] = entropy->mv_probs[i][6 + 2]; - dst[7] = 0; /*unused */ - dst += 8; - } - - /* coeff probs (header part) */ - dst = ctx->vp8_dec.prob_tbl.cpu; - dst += (8 * 7); - for (i = 0; i < 4; ++i) { - for (j = 0; j < 8; ++j) { - for (k = 0; k < 3; ++k) { - dst[0] = entropy->coeff_probs[i][j][k][0]; - dst[1] = entropy->coeff_probs[i][j][k][1]; - dst[2] = entropy->coeff_probs[i][j][k][2]; - dst[3] = entropy->coeff_probs[i][j][k][3]; - dst += 4; - } - } - } - - /* coeff probs (footer part) */ - dst = ctx->vp8_dec.prob_tbl.cpu; - dst += (8 * 55); - for (i = 0; i < 4; ++i) { - for (j = 0; j < 8; ++j) { - for (k = 0; k < 3; ++k) { - dst[0] = entropy->coeff_probs[i][j][k][4]; - dst[1] = entropy->coeff_probs[i][j][k][5]; - dst[2] = entropy->coeff_probs[i][j][k][6]; - dst[3] = entropy->coeff_probs[i][j][k][7]; - dst[4] = entropy->coeff_probs[i][j][k][8]; - dst[5] = entropy->coeff_probs[i][j][k][9]; - dst[6] = entropy->coeff_probs[i][j][k][10]; - dst[7] = 0; /*unused */ - dst += 8; - } - } - } -} - -int hantro_vp8_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_aux_buf *aux_buf; - unsigned int mb_width, mb_height; - size_t segment_map_size; - int ret; - - /* segment map table size calculation */ - mb_width = DIV_ROUND_UP(ctx->dst_fmt.width, 16); - mb_height = DIV_ROUND_UP(ctx->dst_fmt.height, 16); - segment_map_size = round_up(DIV_ROUND_UP(mb_width * mb_height, 4), 64); - - /* - * In context init the dma buffer for segment map must be allocated. - * And the data in segment map buffer must be set to all zero. - */ - aux_buf = &ctx->vp8_dec.segment_map; - aux_buf->size = segment_map_size; - aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, - &aux_buf->dma, GFP_KERNEL); - if (!aux_buf->cpu) - return -ENOMEM; - - /* - * Allocate probability table buffer, - * total 1208 bytes, 4K page is far enough. - */ - aux_buf = &ctx->vp8_dec.prob_tbl; - aux_buf->size = sizeof(struct vp8_prob_tbl_packed); - aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, - &aux_buf->dma, GFP_KERNEL); - if (!aux_buf->cpu) { - ret = -ENOMEM; - goto err_free_seg_map; - } - - return 0; - -err_free_seg_map: - dma_free_coherent(vpu->dev, ctx->vp8_dec.segment_map.size, - ctx->vp8_dec.segment_map.cpu, - ctx->vp8_dec.segment_map.dma); - - return ret; -} - -void hantro_vp8_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_vp8_dec_hw_ctx *vp8_dec = &ctx->vp8_dec; - struct hantro_dev *vpu = ctx->dev; - - dma_free_coherent(vpu->dev, vp8_dec->segment_map.size, - vp8_dec->segment_map.cpu, vp8_dec->segment_map.dma); - dma_free_coherent(vpu->dev, vp8_dec->prob_tbl.size, - vp8_dec->prob_tbl.cpu, vp8_dec->prob_tbl.dma); -} diff --git a/drivers/staging/media/hantro/hantro_vp9.c b/drivers/staging/media/hantro/hantro_vp9.c deleted file mode 100644 index 566cd376c097..000000000000 --- a/drivers/staging/media/hantro/hantro_vp9.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ - -#include -#include - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_vp9.h" - -#define POW2(x) (1 << (x)) - -#define MAX_LOG2_TILE_COLUMNS 6 -#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS) -#define MAX_TILE_COLS 20 -#define MAX_TILE_ROWS 22 - -static size_t hantro_vp9_tile_filter_size(unsigned int height) -{ - u32 h, height32, size; - - h = roundup(height, 8); - - height32 = roundup(h, 64); - size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 8 + 8 */ - - return size; -} - -static size_t hantro_vp9_bsd_control_size(unsigned int height) -{ - u32 h, height32; - - h = roundup(height, 8); - height32 = roundup(h, 64); - - return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1); -} - -static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int height) -{ - u32 w, h; - int num_ctbs; - - w = roundup(width, 8); - h = roundup(height, 8); - num_ctbs = ((w + 63) / 64) * ((h + 63) / 64); - - return num_ctbs * 32; -} - -static inline size_t hantro_vp9_prob_tab_size(void) -{ - return roundup(sizeof(struct hantro_g2_all_probs), 16); -} - -static inline size_t hantro_vp9_count_tab_size(void) -{ - return roundup(sizeof(struct symbol_counts), 16); -} - -static inline size_t hantro_vp9_tile_info_size(void) -{ - return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 15 + 16) & ~0xf, 16); -} - -static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, int l, int m) -{ - if (i == 0) - return &cnts->count_coeffs[j][k][l][m]; - - if (i == 1) - return &cnts->count_coeffs8x8[j][k][l][m]; - - if (i == 2) - return &cnts->count_coeffs16x16[j][k][l][m]; - - if (i == 3) - return &cnts->count_coeffs32x32[j][k][l][m]; - - return NULL; -} - -static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int l, int m) -{ - if (i == 0) - return &cnts->count_coeffs[j][k][l][m][3]; - - if (i == 1) - return &cnts->count_coeffs8x8[j][k][l][m][3]; - - if (i == 2) - return &cnts->count_coeffs16x16[j][k][l][m][3]; - - if (i == 3) - return &cnts->count_coeffs32x32[j][k][l][m][3]; - - return NULL; -} - -#define INNER_LOOP \ - do { \ - for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0][0]); ++m) { \ - vp9_ctx->cnts.coeff[i][j][k][l][m] = \ - get_coeffs_arr(cnts, i, j, k, l, m); \ - vp9_ctx->cnts.eob[i][j][k][l][m][0] = \ - &cnts->count_eobs[i][j][k][l][m]; \ - vp9_ctx->cnts.eob[i][j][k][l][m][1] = \ - get_eobs1(cnts, i, j, k, l, m); \ - } \ - } while (0) - -static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; - int i, j, k, l, m; - - vp9_ctx->cnts.partition = &cnts->partition_counts; - vp9_ctx->cnts.skip = &cnts->mbskip_count; - vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count; - vp9_ctx->cnts.tx32p = &cnts->tx32x32_count; - /* - * g2 hardware uses tx16x16_count[2][3], while the api - * expects tx16p[2][4], so this must be explicitly copied - * into vp9_ctx->cnts.tx16p when passing the data to the - * vp9 library function - */ - vp9_ctx->cnts.tx8p = &cnts->tx8x8_count; - - vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts; - vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts; - vp9_ctx->cnts.comp = &cnts->comp_inter_count; - vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count; - vp9_ctx->cnts.single_ref = &cnts->single_ref_count; - vp9_ctx->cnts.filter = &cnts->switchable_interp_counts; - vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints; - vp9_ctx->cnts.sign = &cnts->mv_counts.sign; - vp9_ctx->cnts.classes = &cnts->mv_counts.classes; - vp9_ctx->cnts.class0 = &cnts->mv_counts.class0; - vp9_ctx->cnts.bits = &cnts->mv_counts.bits; - vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp; - vp9_ctx->cnts.fp = &cnts->mv_counts.fp; - vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp; - vp9_ctx->cnts.hp = &cnts->mv_counts.hp; - - for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i) - for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j) - for (k = 0; k < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0]); ++k) - for (l = 0; l < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l) - INNER_LOOP; -} - -int hantro_vp9_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_variant *variant = vpu->variant; - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; - struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; - struct hantro_aux_buf *misc = &vp9_dec->misc; - u32 i, max_width, max_height, size; - - if (variant->num_dec_fmts < 1) - return -EINVAL; - - for (i = 0; i < variant->num_dec_fmts; ++i) - if (variant->dec_fmts[i].fourcc == V4L2_PIX_FMT_VP9_FRAME) - break; - - if (i == variant->num_dec_fmts) - return -EINVAL; - - max_width = vpu->variant->dec_fmts[i].frmsize.max_width; - max_height = vpu->variant->dec_fmts[i].frmsize.max_height; - - size = hantro_vp9_tile_filter_size(max_height); - vp9_dec->bsd_ctrl_offset = size; - size += hantro_vp9_bsd_control_size(max_height); - - tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge->dma, GFP_KERNEL); - if (!tile_edge->cpu) - return -ENOMEM; - - tile_edge->size = size; - memset(tile_edge->cpu, 0, size); - - size = hantro_vp9_segment_map_size(max_width, max_height); - vp9_dec->segment_map_size = size; - size *= 2; /* we need two areas of this size, used alternately */ - - segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map->dma, GFP_KERNEL); - if (!segment_map->cpu) - goto err_segment_map; - - segment_map->size = size; - memset(segment_map->cpu, 0, size); - - size = hantro_vp9_prob_tab_size(); - vp9_dec->ctx_counters_offset = size; - size += hantro_vp9_count_tab_size(); - vp9_dec->tile_info_offset = size; - size += hantro_vp9_tile_info_size(); - - misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, GFP_KERNEL); - if (!misc->cpu) - goto err_misc; - - misc->size = size; - memset(misc->cpu, 0, size); - - init_v4l2_vp9_count_tbl(ctx); - - return 0; - -err_misc: - dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); - -err_segment_map: - dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); - - return -ENOMEM; -} - -void hantro_vp9_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; - struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; - struct hantro_aux_buf *misc = &vp9_dec->misc; - - dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma); - dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); - dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); -} diff --git a/drivers/staging/media/hantro/hantro_vp9.h b/drivers/staging/media/hantro/hantro_vp9.h deleted file mode 100644 index 26b69275f098..000000000000 --- a/drivers/staging/media/hantro/hantro_vp9.h +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ - -struct hantro_g2_mv_probs { - u8 joint[3]; - u8 sign[2]; - u8 class0_bit[2][1]; - u8 fr[2][3]; - u8 class0_hp[2]; - u8 hp[2]; - u8 classes[2][10]; - u8 class0_fr[2][2][3]; - u8 bits[2][10]; -}; - -struct hantro_g2_probs { - u8 inter_mode[7][4]; - u8 is_inter[4]; - u8 uv_mode[10][8]; - u8 tx8[2][1]; - u8 tx16[2][2]; - u8 tx32[2][3]; - u8 y_mode_tail[4][1]; - u8 y_mode[4][8]; - u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */ - u8 uv_mode_tail[10][1]; - u8 interp_filter[4][2]; - u8 comp_mode[5]; - u8 skip[3]; - - u8 pad1[1]; - - struct hantro_g2_mv_probs mv; - - u8 single_ref[5][2]; - u8 comp_ref[5]; - - u8 pad2[17]; - - u8 coef[4][2][2][6][6][4]; -}; - -struct hantro_g2_all_probs { - u8 kf_y_mode_prob[10][10][8]; - - u8 kf_y_mode_prob_tail[10][10][1]; - u8 ref_pred_probs[3]; - u8 mb_segment_tree_probs[7]; - u8 segment_pred_probs[3]; - u8 ref_scores[4]; - u8 prob_comppred[2]; - - u8 pad1[9]; - - u8 kf_uv_mode_prob[10][8]; - u8 kf_uv_mode_prob_tail[10][1]; - - u8 pad2[6]; - - struct hantro_g2_probs probs; -}; - -struct mv_counts { - u32 joints[4]; - u32 sign[2][2]; - u32 classes[2][11]; - u32 class0[2][2]; - u32 bits[2][10][2]; - u32 class0_fp[2][2][4]; - u32 fp[2][4]; - u32 class0_hp[2][2]; - u32 hp[2][2]; -}; - -struct symbol_counts { - u32 inter_mode_counts[7][3][2]; - u32 sb_ymode_counts[4][10]; - u32 uv_mode_counts[10][10]; - u32 partition_counts[16][4]; - u32 switchable_interp_counts[4][3]; - u32 intra_inter_count[4][2]; - u32 comp_inter_count[5][2]; - u32 single_ref_count[5][2][2]; - u32 comp_ref_count[5][2]; - u32 tx32x32_count[2][4]; - u32 tx16x16_count[2][3]; - u32 tx8x8_count[2][2]; - u32 mbskip_count[3][2]; - - struct mv_counts mv_counts; - - u32 count_coeffs[2][2][6][6][4]; - u32 count_coeffs8x8[2][2][6][6][4]; - u32 count_coeffs16x16[2][2][6][6][4]; - u32 count_coeffs32x32[2][2][6][6][4]; - - u32 count_eobs[4][2][2][6][6]; -}; diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c deleted file mode 100644 index 77f574fdfa77..000000000000 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ /dev/null @@ -1,373 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2019 Pengutronix, Philipp Zabel - */ - -#include -#include - -#include "hantro.h" -#include "hantro_jpeg.h" -#include "hantro_g1_regs.h" -#include "hantro_g2_regs.h" - -#define CTRL_SOFT_RESET 0x00 -#define RESET_G1 BIT(1) -#define RESET_G2 BIT(0) - -#define CTRL_CLOCK_ENABLE 0x04 -#define CLOCK_G1 BIT(1) -#define CLOCK_G2 BIT(0) - -#define CTRL_G1_DEC_FUSE 0x08 -#define CTRL_G1_PP_FUSE 0x0c -#define CTRL_G2_DEC_FUSE 0x10 - -static void imx8m_soft_reset(struct hantro_dev *vpu, u32 reset_bits) -{ - u32 val; - - /* Assert */ - val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); - val &= ~reset_bits; - writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); - - udelay(2); - - /* Release */ - val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); - val |= reset_bits; - writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); -} - -static void imx8m_clk_enable(struct hantro_dev *vpu, u32 clock_bits) -{ - u32 val; - - val = readl(vpu->ctrl_base + CTRL_CLOCK_ENABLE); - val |= clock_bits; - writel(val, vpu->ctrl_base + CTRL_CLOCK_ENABLE); -} - -static int imx8mq_runtime_resume(struct hantro_dev *vpu) -{ - int ret; - - ret = clk_bulk_prepare_enable(vpu->variant->num_clocks, vpu->clocks); - if (ret) { - dev_err(vpu->dev, "Failed to enable clocks\n"); - return ret; - } - - imx8m_soft_reset(vpu, RESET_G1 | RESET_G2); - imx8m_clk_enable(vpu, CLOCK_G1 | CLOCK_G2); - - /* Set values of the fuse registers */ - writel(0xffffffff, vpu->ctrl_base + CTRL_G1_DEC_FUSE); - writel(0xffffffff, vpu->ctrl_base + CTRL_G1_PP_FUSE); - writel(0xffffffff, vpu->ctrl_base + CTRL_G2_DEC_FUSE); - - clk_bulk_disable_unprepare(vpu->variant->num_clocks, vpu->clocks); - - return 0; -} - -/* - * Supported formats. - */ - -static const struct hantro_fmt imx8m_vpu_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12_4L4, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_HEVC_SLICE, - .codec_mode = HANTRO_MODE_HEVC_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9_FRAME, - .codec_mode = HANTRO_MODE_VP9_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, - }, - }, -}; - -static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, G1_REG_INTERRUPT); - state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static int imx8mq_vpu_hw_init(struct hantro_dev *vpu) -{ - vpu->ctrl_base = vpu->reg_bases[vpu->variant->num_regs - 1]; - - return 0; -} - -static void imx8m_vpu_g1_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - imx8m_soft_reset(vpu, RESET_G1); -} - -/* - * Supported codec ops. - */ - -static const struct hantro_codec_ops imx8mq_vpu_codec_ops[] = { - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = imx8m_vpu_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = imx8m_vpu_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = imx8m_vpu_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, -}; - -static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = { - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, -}; - -static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = { - [HANTRO_MODE_HEVC_DEC] = { - .run = hantro_g2_hevc_dec_run, - .init = hantro_hevc_dec_init, - .exit = hantro_hevc_dec_exit, - }, - [HANTRO_MODE_VP9_DEC] = { - .run = hantro_g2_vp9_dec_run, - .done = hantro_g2_vp9_dec_done, - .init = hantro_vp9_dec_init, - .exit = hantro_vp9_dec_exit, - }, -}; - -/* - * VPU variants. - */ - -static const struct hantro_irq imx8mq_irqs[] = { - { "g1", imx8m_vpu_g1_irq }, -}; - -static const struct hantro_irq imx8mq_g2_irqs[] = { - { "g2", hantro_g2_irq }, -}; - -static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; -static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; -static const char * const imx8mq_g1_clk_names[] = { "g1" }; -static const char * const imx8mq_g2_clk_names[] = { "g2" }; - -const struct hantro_variant imx8mq_vpu_variant = { - .dec_fmts = imx8m_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), - .postproc_fmts = imx8m_vpu_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = imx8mq_vpu_codec_ops, - .init = imx8mq_vpu_hw_init, - .runtime_resume = imx8mq_runtime_resume, - .irqs = imx8mq_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_irqs), - .clk_names = imx8mq_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_clk_names), - .reg_names = imx8mq_reg_names, - .num_regs = ARRAY_SIZE(imx8mq_reg_names) -}; - -const struct hantro_variant imx8mq_vpu_g1_variant = { - .dec_fmts = imx8m_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), - .postproc_fmts = imx8m_vpu_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = imx8mq_vpu_g1_codec_ops, - .irqs = imx8mq_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_irqs), - .clk_names = imx8mq_g1_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), -}; - -const struct hantro_variant imx8mq_vpu_g2_variant = { - .dec_offset = 0x0, - .dec_fmts = imx8m_vpu_g2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts), - .postproc_fmts = imx8m_vpu_g2_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_g2_postproc_fmts), - .postproc_ops = &hantro_g2_postproc_ops, - .codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER, - .codec_ops = imx8mq_vpu_g2_codec_ops, - .irqs = imx8mq_g2_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_g2_irqs), - .clk_names = imx8mq_g2_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names), -}; - -const struct hantro_variant imx8mm_vpu_g1_variant = { - .dec_fmts = imx8m_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = imx8mq_vpu_g1_codec_ops, - .irqs = imx8mq_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_irqs), - .clk_names = imx8mq_g1_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), -}; diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c deleted file mode 100644 index 46c1a83bcc4e..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c +++ /dev/null @@ -1,491 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong - * Herman Chen - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa - */ - -#include -#include - -#include - -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -#define VDPU_SWREG(nr) ((nr) * 4) - -#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) -#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) -#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) -#define VDPU_REG_DIR_MV_BASE VDPU_SWREG(62) -#define VDPU_REG_REFER_BASE(i) (VDPU_SWREG(84 + (i))) -#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) -#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_PIC_FIXED_QUANT(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) - -#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) -#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) -#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) - -#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) -#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_START_CODE_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_CH_8PIX_ILEAV_E(v) ((v) ? BIT(21) : 0) -#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) -#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) -#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) -#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) -#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_SEQ_MBAFF_E(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_PICORD_COUNT_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) - -#define VDPU_REG_PRED_BC_TAP_0_0(v) (((v) << 22) & GENMASK(31, 22)) -#define VDPU_REG_PRED_BC_TAP_0_1(v) (((v) << 12) & GENMASK(21, 12)) -#define VDPU_REG_PRED_BC_TAP_0_2(v) (((v) << 2) & GENMASK(11, 2)) - -#define VDPU_REG_REFBU_E(v) ((v) ? BIT(31) : 0) - -#define VDPU_REG_PINIT_RLIST_F9(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_PINIT_RLIST_F8(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_PINIT_RLIST_F7(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F6(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F5(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F4(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_PINIT_RLIST_F15(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_PINIT_RLIST_F14(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_PINIT_RLIST_F13(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F12(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F11(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F10(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_REFER1_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER0_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER3_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER2_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER5_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER4_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER7_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER6_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER9_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER8_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER11_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER10_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER13_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER12_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER15_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER14_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_BINIT_RLIST_F5(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_F4(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_F11(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_F10(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_F9(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F8(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F7(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F6(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_F15(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F14(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F13(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F12(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B5(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_B4(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_B3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B11(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_B10(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_B9(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B8(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B7(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B6(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B15(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B14(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B13(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B12(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_PINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_REFER_LTERM_E(v) (((v) << 0) & GENMASK(31, 0)) - -#define VDPU_REG_REFER_VALID_E(v) (((v) << 0) & GENMASK(31, 0)) - -#define VDPU_REG_STRM_START_BIT(v) (((v) << 0) & GENMASK(5, 0)) - -#define VDPU_REG_CH_QP_OFFSET2(v) (((v) << 22) & GENMASK(26, 22)) -#define VDPU_REG_CH_QP_OFFSET(v) (((v) << 17) & GENMASK(21, 17)) -#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 9) & GENMASK(16, 9)) -#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) - -#define VDPU_REG_WEIGHT_BIPR_IDC(v) (((v) << 16) & GENMASK(17, 16)) -#define VDPU_REG_REF_FRAMES(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_FILT_CTRL_PRES(v) ((v) ? BIT(31) : 0) -#define VDPU_REG_RDPIC_CNT_PRES(v) ((v) ? BIT(30) : 0) -#define VDPU_REG_FRAMENUM_LEN(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_FRAMENUM(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFPIC_MK_LEN(v) (((v) << 16) & GENMASK(26, 16)) -#define VDPU_REG_IDR_PIC_ID(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_PPS_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define VDPU_REG_REFIDX1_ACTIVE(v) (((v) << 19) & GENMASK(23, 19)) -#define VDPU_REG_REFIDX0_ACTIVE(v) (((v) << 14) & GENMASK(18, 14)) -#define VDPU_REG_POC_LENGTH(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_IDR_PIC_E(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_DIR_8X8_INFER_E(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_BLACKWHITE_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_CABAC_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_WEIGHT_PRED_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_CONST_INTRA_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_8X8TRANS_FLAG_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_TYPE1_QUANT_E(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_FIELDPIC_FLAG_E(v) ((v) ? BIT(0) : 0) - -static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - - reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | - VDPU_REG_DEC_SCMD_DIS(0) | - VDPU_REG_FILTERING_DIS(0) | - VDPU_REG_PIC_FIXED_QUANT(0) | - VDPU_REG_DEC_LATENCY(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); - - reg = VDPU_REG_INIT_QP(pps->pic_init_qp_minus26 + 26) | - VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); - - reg = VDPU_REG_APF_THRESHOLD(8) | - VDPU_REG_STARTMB_X(0) | - VDPU_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); - - reg = VDPU_REG_DEC_MODE(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); - - reg = VDPU_REG_DEC_STRENDIAN_E(1) | - VDPU_REG_DEC_STRSWAP32_E(1) | - VDPU_REG_DEC_OUTSWAP32_E(1) | - VDPU_REG_DEC_INSWAP32_E(1) | - VDPU_REG_DEC_OUT_ENDIAN(1) | - VDPU_REG_DEC_IN_ENDIAN(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); - - reg = VDPU_REG_DEC_DATA_DISC_E(0) | - VDPU_REG_DEC_MAX_BURST(16) | - VDPU_REG_DEC_AXI_WR_ID(0) | - VDPU_REG_DEC_AXI_RD_ID(0xff); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); - - reg = VDPU_REG_START_CODE_E(1) | - VDPU_REG_CH_8PIX_ILEAV_E(0) | - VDPU_REG_RLC_MODE_E(0) | - VDPU_REG_PIC_INTERLACE_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && - (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || - dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) | - VDPU_REG_PIC_FIELDMODE_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) | - VDPU_REG_PIC_TOPFIELD_E(!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) | - VDPU_REG_WRITE_MVS_E((sps->profile_idc > 66) && dec_param->nal_ref_idc) | - VDPU_REG_SEQ_MBAFF_E(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) | - VDPU_REG_PICORD_COUNT_E(sps->profile_idc > 66) | - VDPU_REG_DEC_TIMEOUT_E(1) | - VDPU_REG_DEC_CLK_GATE_E(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); - - reg = VDPU_REG_PRED_BC_TAP_0_0(1) | - VDPU_REG_PRED_BC_TAP_0_1((u32)-5) | - VDPU_REG_PRED_BC_TAP_0_2(20); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(59)); - - reg = VDPU_REG_REFBU_E(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(65)); - - reg = VDPU_REG_STRM_START_BIT(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(109)); - - reg = VDPU_REG_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset) | - VDPU_REG_CH_QP_OFFSET(pps->chroma_qp_index_offset) | - VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | - VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(110)); - - reg = VDPU_REG_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc) | - VDPU_REG_REF_FRAMES(sps->max_num_ref_frames); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(111)); - - reg = VDPU_REG_FILT_CTRL_PRES(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) | - VDPU_REG_RDPIC_CNT_PRES(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) | - VDPU_REG_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | - VDPU_REG_FRAMENUM(dec_param->frame_num); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(112)); - - reg = VDPU_REG_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | - VDPU_REG_IDR_PIC_ID(dec_param->idr_pic_id); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(113)); - - reg = VDPU_REG_PPS_ID(pps->pic_parameter_set_id) | - VDPU_REG_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | - VDPU_REG_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | - VDPU_REG_POC_LENGTH(dec_param->pic_order_cnt_bit_size); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(114)); - - reg = VDPU_REG_IDR_PIC_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) | - VDPU_REG_DIR_8X8_INFER_E(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) | - VDPU_REG_BLACKWHITE_E(sps->profile_idc >= 100 && sps->chroma_format_idc == 0) | - VDPU_REG_CABAC_E(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) | - VDPU_REG_WEIGHT_PRED_E(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) | - VDPU_REG_CONST_INTRA_E(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) | - VDPU_REG_8X8TRANS_FLAG_E(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) | - VDPU_REG_TYPE1_QUANT_E(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) | - VDPU_REG_FIELDPIC_FLAG_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(115)); -} - -static void set_ref(struct hantro_ctx *ctx) -{ - const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - int i; - - b0_reflist = ctx->h264_dec.reflists.b0; - b1_reflist = ctx->h264_dec.reflists.b1; - p_reflist = ctx->h264_dec.reflists.p; - - reg = VDPU_REG_PINIT_RLIST_F9(p_reflist[9].index) | - VDPU_REG_PINIT_RLIST_F8(p_reflist[8].index) | - VDPU_REG_PINIT_RLIST_F7(p_reflist[7].index) | - VDPU_REG_PINIT_RLIST_F6(p_reflist[6].index) | - VDPU_REG_PINIT_RLIST_F5(p_reflist[5].index) | - VDPU_REG_PINIT_RLIST_F4(p_reflist[4].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(74)); - - reg = VDPU_REG_PINIT_RLIST_F15(p_reflist[15].index) | - VDPU_REG_PINIT_RLIST_F14(p_reflist[14].index) | - VDPU_REG_PINIT_RLIST_F13(p_reflist[13].index) | - VDPU_REG_PINIT_RLIST_F12(p_reflist[12].index) | - VDPU_REG_PINIT_RLIST_F11(p_reflist[11].index) | - VDPU_REG_PINIT_RLIST_F10(p_reflist[10].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(75)); - - reg = VDPU_REG_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, 1)) | - VDPU_REG_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(76)); - - reg = VDPU_REG_REFER3_NBR(hantro_h264_get_ref_nbr(ctx, 3)) | - VDPU_REG_REFER2_NBR(hantro_h264_get_ref_nbr(ctx, 2)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(77)); - - reg = VDPU_REG_REFER5_NBR(hantro_h264_get_ref_nbr(ctx, 5)) | - VDPU_REG_REFER4_NBR(hantro_h264_get_ref_nbr(ctx, 4)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(78)); - - reg = VDPU_REG_REFER7_NBR(hantro_h264_get_ref_nbr(ctx, 7)) | - VDPU_REG_REFER6_NBR(hantro_h264_get_ref_nbr(ctx, 6)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(79)); - - reg = VDPU_REG_REFER9_NBR(hantro_h264_get_ref_nbr(ctx, 9)) | - VDPU_REG_REFER8_NBR(hantro_h264_get_ref_nbr(ctx, 8)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(80)); - - reg = VDPU_REG_REFER11_NBR(hantro_h264_get_ref_nbr(ctx, 11)) | - VDPU_REG_REFER10_NBR(hantro_h264_get_ref_nbr(ctx, 10)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(81)); - - reg = VDPU_REG_REFER13_NBR(hantro_h264_get_ref_nbr(ctx, 13)) | - VDPU_REG_REFER12_NBR(hantro_h264_get_ref_nbr(ctx, 12)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(82)); - - reg = VDPU_REG_REFER15_NBR(hantro_h264_get_ref_nbr(ctx, 15)) | - VDPU_REG_REFER14_NBR(hantro_h264_get_ref_nbr(ctx, 14)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(83)); - - reg = VDPU_REG_BINIT_RLIST_F5(b0_reflist[5].index) | - VDPU_REG_BINIT_RLIST_F4(b0_reflist[4].index) | - VDPU_REG_BINIT_RLIST_F3(b0_reflist[3].index) | - VDPU_REG_BINIT_RLIST_F2(b0_reflist[2].index) | - VDPU_REG_BINIT_RLIST_F1(b0_reflist[1].index) | - VDPU_REG_BINIT_RLIST_F0(b0_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(100)); - - reg = VDPU_REG_BINIT_RLIST_F11(b0_reflist[11].index) | - VDPU_REG_BINIT_RLIST_F10(b0_reflist[10].index) | - VDPU_REG_BINIT_RLIST_F9(b0_reflist[9].index) | - VDPU_REG_BINIT_RLIST_F8(b0_reflist[8].index) | - VDPU_REG_BINIT_RLIST_F7(b0_reflist[7].index) | - VDPU_REG_BINIT_RLIST_F6(b0_reflist[6].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(101)); - - reg = VDPU_REG_BINIT_RLIST_F15(b0_reflist[15].index) | - VDPU_REG_BINIT_RLIST_F14(b0_reflist[14].index) | - VDPU_REG_BINIT_RLIST_F13(b0_reflist[13].index) | - VDPU_REG_BINIT_RLIST_F12(b0_reflist[12].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(102)); - - reg = VDPU_REG_BINIT_RLIST_B5(b1_reflist[5].index) | - VDPU_REG_BINIT_RLIST_B4(b1_reflist[4].index) | - VDPU_REG_BINIT_RLIST_B3(b1_reflist[3].index) | - VDPU_REG_BINIT_RLIST_B2(b1_reflist[2].index) | - VDPU_REG_BINIT_RLIST_B1(b1_reflist[1].index) | - VDPU_REG_BINIT_RLIST_B0(b1_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(103)); - - reg = VDPU_REG_BINIT_RLIST_B11(b1_reflist[11].index) | - VDPU_REG_BINIT_RLIST_B10(b1_reflist[10].index) | - VDPU_REG_BINIT_RLIST_B9(b1_reflist[9].index) | - VDPU_REG_BINIT_RLIST_B8(b1_reflist[8].index) | - VDPU_REG_BINIT_RLIST_B7(b1_reflist[7].index) | - VDPU_REG_BINIT_RLIST_B6(b1_reflist[6].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(104)); - - reg = VDPU_REG_BINIT_RLIST_B15(b1_reflist[15].index) | - VDPU_REG_BINIT_RLIST_B14(b1_reflist[14].index) | - VDPU_REG_BINIT_RLIST_B13(b1_reflist[13].index) | - VDPU_REG_BINIT_RLIST_B12(b1_reflist[12].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(105)); - - reg = VDPU_REG_PINIT_RLIST_F3(p_reflist[3].index) | - VDPU_REG_PINIT_RLIST_F2(p_reflist[2].index) | - VDPU_REG_PINIT_RLIST_F1(p_reflist[1].index) | - VDPU_REG_PINIT_RLIST_F0(p_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(106)); - - reg = VDPU_REG_REFER_LTERM_E(ctx->h264_dec.dpb_longterm); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(107)); - - reg = VDPU_REG_REFER_VALID_E(ctx->h264_dec.dpb_valid); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(108)); - - /* Set up addresses of DPB buffers. */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { - dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); - - vdpu_write_relaxed(vpu, dma_addr, VDPU_REG_REFER_BASE(i)); - } -} - -static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - struct vb2_v4l2_buffer *dst_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma, dst_dma; - size_t offset = 0; - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - vdpu_write_relaxed(vpu, src_dma, VDPU_REG_RLC_VLC_BASE); - - /* Destination (decoded frame) buffer. */ - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); - /* Adjust dma addr to start at second line for bottom field */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset = ALIGN(ctx->src_fmt.width, MB_DIM); - vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DEC_OUT_BASE); - - /* Higher profiles require DMV buffer appended to reference frames. */ - if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { - unsigned int bytes_per_mb = 384; - - /* DMV buffer for monochrome start directly after Y-plane */ - if (ctrls->sps->profile_idc >= 100 && - ctrls->sps->chroma_format_idc == 0) - bytes_per_mb = 256; - offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - - /* - * DMV buffer is split in two for field encoded frames, - * adjust offset for bottom field - */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset += 32 * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DIR_MV_BASE); - } - - /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ - vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, VDPU_REG_QTABLE_BASE); -} - -int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf; - u32 reg; - int ret; - - /* Prepare the H264 decoder context. */ - ret = hantro_h264_dec_prepare_run(ctx); - if (ret) - return ret; - - src_buf = hantro_get_src_buf(ctx); - set_params(ctx, src_buf); - set_ref(ctx); - set_buffers(ctx, src_buf); - - hantro_end_prepare_run(ctx); - - /* Start decoding! */ - reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); - vdpu_write(vpu, reg, VDPU_SWREG(57)); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c deleted file mode 100644 index 8395c4d48dd0..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * - * JPEG encoder - * ------------ - * The VPU JPEG encoder produces JPEG baseline sequential format. - * The quantization coefficients are 8-bit values, complying with - * the baseline specification. Therefore, it requires - * luma and chroma quantization tables. The hardware does entropy - * encoding using internal Huffman tables, as specified in the JPEG - * specification. - * - * In other words, only the luma and chroma quantization tables are - * required for the encoding operation. - * - * Quantization luma table values are written to registers - * VEPU_swreg_0-VEPU_swreg_15, and chroma table values to - * VEPU_swreg_16-VEPU_swreg_31. A special order is needed, neither - * zigzag, nor linear. - */ - -#include -#include -#include "hantro_jpeg.h" -#include "hantro.h" -#include "hantro_v4l2.h" -#include "hantro_hw.h" -#include "rockchip_vpu2_regs.h" - -#define VEPU_JPEG_QUANT_TABLE_COUNT 16 - -static void rockchip_vpu2_set_src_img_ctrl(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - u32 overfill_r, overfill_b; - u32 reg; - - /* - * The format width and height are already macroblock aligned - * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination - * format width and height can be further modified by - * .vidioc_s_selection(), and the width is 4-aligned. - */ - overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; - overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; - - reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width); - vepu_write_relaxed(vpu, reg, VEPU_REG_INPUT_LUMA_INFO); - - reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) | - VEPU_REG_IN_IMG_CTRL_OVRFLB(overfill_b); - /* - * This register controls the input crop, as the offset - * from the right/bottom within the last macroblock. The offset from the - * right must be divided by 4 and so the crop must be aligned to 4 pixels - * horizontally. - */ - vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_OVER_FILL_STRM_OFFSET); - - reg = VEPU_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); - vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_CTRL1); -} - -static void rockchip_vpu2_jpeg_enc_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf) -{ - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; - dma_addr_t src[3]; - u32 size_left; - - size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; - if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) - size_left = 0; - - WARN_ON(pix_fmt->num_planes > 3); - - vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + - ctx->vpu_dst_fmt->header_size, - VEPU_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, size_left, VEPU_REG_STR_BUF_LIMIT); - - if (pix_fmt->num_planes == 1) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - } else if (pix_fmt->num_planes == 2) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); - } else { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); - vepu_write_relaxed(vpu, src[2], VEPU_REG_ADDR_IN_PLANE_2); - } -} - -static void -rockchip_vpu2_jpeg_enc_set_qtable(struct hantro_dev *vpu, - unsigned char *luma_qtable, - unsigned char *chroma_qtable) -{ - u32 reg, i; - __be32 *luma_qtable_p; - __be32 *chroma_qtable_p; - - luma_qtable_p = (__be32 *)luma_qtable; - chroma_qtable_p = (__be32 *)chroma_qtable; - - /* - * Quantization table registers must be written in contiguous blocks. - * DO NOT collapse the below two "for" loops into one. - */ - for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable_p[i]); - vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); - } - - for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&chroma_qtable_p[i]); - vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); - } -} - -int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct hantro_jpeg_ctx jpeg_ctx; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - if (!jpeg_ctx.buffer) - return -ENOMEM; - - jpeg_ctx.width = ctx->dst_fmt.width; - jpeg_ctx.height = ctx->dst_fmt.height; - jpeg_ctx.quality = ctx->jpeg_quality; - hantro_jpeg_header_assemble(&jpeg_ctx); - - /* Switch to JPEG encoder mode before writing registers */ - vepu_write_relaxed(vpu, VEPU_REG_ENCODE_FORMAT_JPEG, - VEPU_REG_ENCODE_START); - - rockchip_vpu2_set_src_img_ctrl(vpu, ctx); - rockchip_vpu2_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf); - rockchip_vpu2_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, - jpeg_ctx.hw_chroma_qtable); - - reg = VEPU_REG_OUTPUT_SWAP32 - | VEPU_REG_OUTPUT_SWAP16 - | VEPU_REG_OUTPUT_SWAP8 - | VEPU_REG_INPUT_SWAP8 - | VEPU_REG_INPUT_SWAP16 - | VEPU_REG_INPUT_SWAP32; - /* Make sure that all registers are written at this point. */ - vepu_write(vpu, reg, VEPU_REG_DATA_ENDIAN); - - reg = VEPU_REG_AXI_CTRL_BURST_LEN(16); - vepu_write_relaxed(vpu, reg, VEPU_REG_AXI_CTRL); - - reg = VEPU_REG_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) - | VEPU_REG_MB_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) - | VEPU_REG_FRAME_TYPE_INTRA - | VEPU_REG_ENCODE_FORMAT_JPEG - | VEPU_REG_ENCODE_ENABLE; - - /* Kick the watchdog and start encoding */ - hantro_end_prepare_run(ctx); - vepu_write(vpu, reg, VEPU_REG_ENCODE_START); - - return 0; -} - -void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - u32 bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; - struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - ctx->vpu_dst_fmt->header_size + bytesused); -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c deleted file mode 100644 index b66737fab46b..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include -#include -#include -#include "hantro.h" -#include "hantro_hw.h" - -#define VDPU_SWREG(nr) ((nr) * 4) - -#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) -#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) -#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) -#define VDPU_REG_REFER0_BASE VDPU_SWREG(131) -#define VDPU_REG_REFER2_BASE VDPU_SWREG(134) -#define VDPU_REG_REFER3_BASE VDPU_SWREG(135) -#define VDPU_REG_REFER1_BASE VDPU_SWREG(148) -#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) -#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) - -#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) -#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) -#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) - -#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) -#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) -#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) -#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) -#define VDPU_REG_PIC_B_E(v) ((v) ? BIT(15) : 0) -#define VDPU_REG_PIC_INTER_E(v) ((v) ? BIT(14) : 0) -#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) -#define VDPU_REG_FWD_INTERLACE_E(v) ((v) ? BIT(12) : 0) -#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) - -#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) -#define VDPU_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) - -#define VDPU_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) -#define VDPU_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) -#define VDPU_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) -#define VDPU_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) -#define VDPU_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) -#define VDPU_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) -#define VDPU_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) -#define VDPU_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) -#define VDPU_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) - -static void -rockchip_vpu2_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - struct v4l2_ctrl_mpeg2_quantisation *q; - - q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); - hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); - vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, VDPU_REG_QTABLE_BASE); -} - -static void -rockchip_vpu2_mpeg2_dec_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf, - const struct v4l2_ctrl_mpeg2_sequence *seq, - const struct v4l2_ctrl_mpeg2_picture *pic) -{ - dma_addr_t forward_addr = 0, backward_addr = 0; - dma_addr_t current_addr, addr; - - switch (pic->picture_coding_type) { - case V4L2_MPEG2_PIC_CODING_TYPE_B: - backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); - fallthrough; - case V4L2_MPEG2_PIC_CODING_TYPE_P: - forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); - } - - /* Source bitstream buffer */ - addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vdpu_write_relaxed(vpu, addr, VDPU_REG_RLC_VLC_BASE); - - /* Destination frame buffer */ - addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - current_addr = addr; - - if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) - addr += ALIGN(ctx->dst_fmt.width, 16); - vdpu_write_relaxed(vpu, addr, VDPU_REG_DEC_OUT_BASE); - - if (!forward_addr) - forward_addr = current_addr; - if (!backward_addr) - backward_addr = current_addr; - - /* Set forward ref frame (top/bottom field) */ - if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || - pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || - (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && - pic->flags & V4L2_MPEG2_PIC_TOP_FIELD) || - (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && - !(pic->flags & V4L2_MPEG2_PIC_TOP_FIELD))) { - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { - vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); - } - - /* Set backward ref frame (top/bottom field) */ - vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER2_BASE); - vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER3_BASE); -} - -int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - const struct v4l2_ctrl_mpeg2_sequence *seq; - const struct v4l2_ctrl_mpeg2_picture *pic; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - seq = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_SEQUENCE); - pic = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_PICTURE); - - reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | - VDPU_REG_DEC_SCMD_DIS(0) | - VDPU_REG_FILTERING_DIS(1) | - VDPU_REG_DEC_LATENCY(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); - - reg = VDPU_REG_INIT_QP(1) | - VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); - - reg = VDPU_REG_APF_THRESHOLD(8) | - VDPU_REG_STARTMB_X(0) | - VDPU_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); - - reg = VDPU_REG_DEC_MODE(5); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); - - reg = VDPU_REG_DEC_STRENDIAN_E(1) | - VDPU_REG_DEC_STRSWAP32_E(1) | - VDPU_REG_DEC_OUTSWAP32_E(1) | - VDPU_REG_DEC_INSWAP32_E(1) | - VDPU_REG_DEC_OUT_ENDIAN(1) | - VDPU_REG_DEC_IN_ENDIAN(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); - - reg = VDPU_REG_DEC_DATA_DISC_E(0) | - VDPU_REG_DEC_MAX_BURST(16) | - VDPU_REG_DEC_AXI_WR_ID(0) | - VDPU_REG_DEC_AXI_RD_ID(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); - - reg = VDPU_REG_RLC_MODE_E(0) | - VDPU_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | - VDPU_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | - VDPU_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | - VDPU_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | - VDPU_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | - VDPU_REG_FWD_INTERLACE_E(0) | - VDPU_REG_WRITE_MVS_E(0) | - VDPU_REG_DEC_TIMEOUT_E(1) | - VDPU_REG_DEC_CLK_GATE_E(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); - - reg = VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | - VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | - VDPU_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - VDPU_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(120)); - - reg = VDPU_REG_STRM_START_BIT(0) | - VDPU_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | - VDPU_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | - VDPU_REG_INTRA_DC_PREC(pic->intra_dc_precision) | - VDPU_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | - VDPU_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(122)); - - reg = VDPU_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - VDPU_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | - VDPU_REG_FCODE_FWD_VER(pic->f_code[0][1]) | - VDPU_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | - VDPU_REG_FCODE_BWD_VER(pic->f_code[1][1]) | - VDPU_REG_MV_ACCURACY_FWD(1) | - VDPU_REG_MV_ACCURACY_BWD(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(136)); - - rockchip_vpu2_mpeg2_dec_set_quantisation(vpu, ctx); - - rockchip_vpu2_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf, seq, pic); - - /* Kick the watchdog and start decoding */ - hantro_end_prepare_run(ctx); - - reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); - vdpu_write(vpu, reg, VDPU_SWREG(57)); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c deleted file mode 100644 index d079075448c9..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c +++ /dev/null @@ -1,600 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip VPU codec vp8 decode driver - * - * Copyright (C) 2014 Rockchip Electronics Co., Ltd. - * ZhiChao Yu - * - * Copyright (C) 2014 Google LLC. - * Tomasz Figa - * - * Copyright (C) 2015 Rockchip Electronics Co., Ltd. - * Alpha Lin - */ - -#include - -#include "hantro_hw.h" -#include "hantro.h" -#include "hantro_g1_regs.h" - -#define VDPU_REG_DEC_CTRL0 0x0c8 -#define VDPU_REG_STREAM_LEN 0x0cc -#define VDPU_REG_DEC_FORMAT 0x0d4 -#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) -#define VDPU_REG_DATA_ENDIAN 0x0d8 -#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) -#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) -#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) -#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) -#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) -#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) -#define VDPU_REG_AXI_CTRL 0x0e0 -#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) -#define VDPU_REG_EN_FLAGS 0x0e4 -#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) -#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) -#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) -#define VDPU_REG_PRED_FLT 0x0ec -#define VDPU_REG_ADDR_QTABLE 0x0f4 -#define VDPU_REG_ADDR_DST 0x0fc -#define VDPU_REG_ADDR_STR 0x100 -#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 -#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 -#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define VDPU_REG_VP8_CTRL0 0x1e8 -#define VDPU_REG_VP8_DATA_VAL 0x1f0 -#define VDPU_REG_PRED_FLT7 0x1f4 -#define VDPU_REG_PRED_FLT8 0x1f8 -#define VDPU_REG_PRED_FLT9 0x1fc -#define VDPU_REG_PRED_FLT10 0x200 -#define VDPU_REG_FILTER_LEVEL 0x204 -#define VDPU_REG_VP8_QUANTER0 0x208 -#define VDPU_REG_VP8_ADDR_REF0 0x20c -#define VDPU_REG_FILTER_MB_ADJ 0x210 -#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define VDPU_REG_FILTER_REF_ADJ 0x214 -#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) -#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_DCT_BASE(i) \ - (0x230 + ((((i) < 5) ? (i) : ((i) + 1)) * 0x4)) -#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 -#define VDPU_REG_VP8_SEGMENT_VAL 0x254 -#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define VDPU_REG_VP8_DCT_START_BIT2 0x258 -#define VDPU_REG_VP8_QUANTER1 0x25c -#define VDPU_REG_VP8_QUANTER2 0x260 -#define VDPU_REG_PRED_FLT1 0x264 -#define VDPU_REG_PRED_FLT2 0x268 -#define VDPU_REG_PRED_FLT3 0x26c -#define VDPU_REG_PRED_FLT4 0x270 -#define VDPU_REG_PRED_FLT5 0x274 -#define VDPU_REG_PRED_FLT6 0x278 - -static const struct hantro_reg vp8_dec_dct_base[8] = { - { VDPU_REG_ADDR_STR, 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(0), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(1), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(2), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(3), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(4), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(5), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(6), 0, 0xffffffff }, -}; - -static const struct hantro_reg vp8_dec_lf_level[4] = { - { VDPU_REG_FILTER_LEVEL, 18, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 12, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 6, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 0, 0x3f }, -}; - -static const struct hantro_reg vp8_dec_mb_adj[4] = { - { VDPU_REG_FILTER_MB_ADJ, 21, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 14, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 7, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 0, 0x7f }, -}; - -static const struct hantro_reg vp8_dec_ref_adj[4] = { - { VDPU_REG_FILTER_REF_ADJ, 21, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 14, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 7, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 0, 0x7f }, -}; - -static const struct hantro_reg vp8_dec_quant[4] = { - { VDPU_REG_VP8_QUANTER0, 11, 0x7ff }, - { VDPU_REG_VP8_QUANTER0, 0, 0x7ff }, - { VDPU_REG_VP8_QUANTER1, 11, 0x7ff }, - { VDPU_REG_VP8_QUANTER1, 0, 0x7ff }, -}; - -static const struct hantro_reg vp8_dec_quant_delta[5] = { - { VDPU_REG_VP8_QUANTER0, 27, 0x1f }, - { VDPU_REG_VP8_QUANTER0, 22, 0x1f }, - { VDPU_REG_VP8_QUANTER1, 27, 0x1f }, - { VDPU_REG_VP8_QUANTER1, 22, 0x1f }, - { VDPU_REG_VP8_QUANTER2, 27, 0x1f }, -}; - -static const struct hantro_reg vp8_dec_dct_start_bits[8] = { - { VDPU_REG_VP8_CTRL0, 26, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT, 26, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT, 20, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 24, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 18, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 12, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 6, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 0, 0x3f }, -}; - -static const struct hantro_reg vp8_dec_pred_bc_tap[8][6] = { - { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT, 22, 0x3ff }, - { VDPU_REG_PRED_FLT, 12, 0x3ff }, - { VDPU_REG_PRED_FLT, 2, 0x3ff }, - { VDPU_REG_PRED_FLT1, 22, 0x3ff }, - { 0, 0, 0}, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT1, 12, 0x3ff }, - { VDPU_REG_PRED_FLT1, 2, 0x3ff }, - { VDPU_REG_PRED_FLT2, 22, 0x3ff }, - { VDPU_REG_PRED_FLT2, 12, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 10, 0x3 }, - { VDPU_REG_PRED_FLT2, 2, 0x3ff }, - { VDPU_REG_PRED_FLT3, 22, 0x3ff }, - { VDPU_REG_PRED_FLT3, 12, 0x3ff }, - { VDPU_REG_PRED_FLT3, 2, 0x3ff }, - { VDPU_REG_PRED_FLT10, 8, 0x3}, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT4, 22, 0x3ff }, - { VDPU_REG_PRED_FLT4, 12, 0x3ff }, - { VDPU_REG_PRED_FLT4, 2, 0x3ff }, - { VDPU_REG_PRED_FLT5, 22, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 6, 0x3 }, - { VDPU_REG_PRED_FLT5, 12, 0x3ff }, - { VDPU_REG_PRED_FLT5, 2, 0x3ff }, - { VDPU_REG_PRED_FLT6, 22, 0x3ff }, - { VDPU_REG_PRED_FLT6, 12, 0x3ff }, - { VDPU_REG_PRED_FLT10, 4, 0x3 }, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT6, 2, 0x3ff }, - { VDPU_REG_PRED_FLT7, 22, 0x3ff }, - { VDPU_REG_PRED_FLT7, 12, 0x3ff }, - { VDPU_REG_PRED_FLT7, 2, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 2, 0x3 }, - { VDPU_REG_PRED_FLT8, 22, 0x3ff }, - { VDPU_REG_PRED_FLT8, 12, 0x3ff }, - { VDPU_REG_PRED_FLT8, 2, 0x3ff }, - { VDPU_REG_PRED_FLT9, 22, 0x3ff }, - { VDPU_REG_PRED_FLT10, 0, 0x3 }, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT9, 12, 0x3ff }, - { VDPU_REG_PRED_FLT9, 2, 0x3ff }, - { VDPU_REG_PRED_FLT10, 22, 0x3ff }, - { VDPU_REG_PRED_FLT10, 12, 0x3ff }, - { 0, 0, 0}, - }, -}; - -static const struct hantro_reg vp8_dec_mb_start_bit = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 18, - .mask = 0x3f -}; - -static const struct hantro_reg vp8_dec_mb_aligned_data_len = { - .base = VDPU_REG_VP8_DATA_VAL, - .shift = 0, - .mask = 0x3fffff -}; - -static const struct hantro_reg vp8_dec_num_dct_partitions = { - .base = VDPU_REG_VP8_DATA_VAL, - .shift = 24, - .mask = 0xf -}; - -static const struct hantro_reg vp8_dec_stream_len = { - .base = VDPU_REG_STREAM_LEN, - .shift = 0, - .mask = 0xffffff -}; - -static const struct hantro_reg vp8_dec_mb_width = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 23, - .mask = 0x1ff -}; - -static const struct hantro_reg vp8_dec_mb_height = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 11, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_mb_width_ext = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 3, - .mask = 0x7 -}; - -static const struct hantro_reg vp8_dec_mb_height_ext = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 0, - .mask = 0x7 -}; - -static const struct hantro_reg vp8_dec_bool_range = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 0, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_bool_value = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 8, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_filter_disable = { - .base = VDPU_REG_DEC_CTRL0, - .shift = 8, - .mask = 1 -}; - -static const struct hantro_reg vp8_dec_skip_mode = { - .base = VDPU_REG_DEC_CTRL0, - .shift = 9, - .mask = 1 -}; - -static const struct hantro_reg vp8_dec_start_dec = { - .base = VDPU_REG_EN_FLAGS, - .shift = 0, - .mask = 1 -}; - -static void cfg_lf(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - const struct v4l2_vp8_loop_filter *lf = &hdr->lf; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - u32 reg; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 lf_level = clamp(lf->level + seg->lf_update[i], - 0, 63); - - hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_lf_level[i], - seg->lf_update[i]); - } - - reg = VDPU_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); - if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) - reg |= VDPU_REG_REF_PIC_FILT_TYPE_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_FILTER_MB_ADJ); - - if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { - for (i = 0; i < 4; i++) { - hantro_reg_write(vpu, &vp8_dec_mb_adj[i], - lf->mb_mode_delta[i]); - hantro_reg_write(vpu, &vp8_dec_ref_adj[i], - lf->ref_frm_delta[i]); - } - } -} - -static void cfg_qp(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_quantization *q = &hdr->quant; - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], - 0, 127); - - hantro_reg_write(vpu, &vp8_dec_quant[i], quant); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_quant[i], - seg->quant_update[i]); - } - - hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); -} - -static void cfg_parts(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_src; - u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; - u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; - u32 dct_size_part_size, dct_part_offset; - dma_addr_t src_dma; - u32 dct_part_total_len = 0; - u32 count = 0; - unsigned int i; - - vb2_src = hantro_get_src_buf(ctx); - src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - /* - * Calculate control partition mb data info - * @first_part_header_bits: bits offset of mb data from first - * part start pos - * @mb_offset_bits: bits offset of mb data from src_dma - * base addr - * @mb_offset_byte: bytes offset of mb data from src_dma - * base addr - * @mb_start_bits: bits offset of mb data from mb data - * 64bits alignment addr - */ - mb_offset_bits = first_part_offset * 8 + - hdr->first_part_header_bits + 8; - mb_offset_bytes = mb_offset_bits / 8; - mb_start_bits = mb_offset_bits - - (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; - mb_size = hdr->first_part_size - - (mb_offset_bytes - first_part_offset) + - (mb_offset_bytes & DEC_8190_ALIGN_MASK); - - /* Macroblock data aligned base addr */ - vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) + - src_dma, VDPU_REG_VP8_ADDR_CTRL_PART); - hantro_reg_write(vpu, &vp8_dec_mb_start_bit, mb_start_bits); - hantro_reg_write(vpu, &vp8_dec_mb_aligned_data_len, mb_size); - - /* - * Calculate DCT partition info - * @dct_size_part_size: Containing sizes of DCT part, every DCT part - * has 3 bytes to store its size, except the last - * DCT part - * @dct_part_offset: bytes offset of DCT parts from src_dma base addr - * @dct_part_total_len: total size of all DCT parts - */ - dct_size_part_size = (hdr->num_dct_parts - 1) * 3; - dct_part_offset = first_part_offset + hdr->first_part_size; - for (i = 0; i < hdr->num_dct_parts; i++) - dct_part_total_len += hdr->dct_part_sizes[i]; - dct_part_total_len += dct_size_part_size; - dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); - - /* Number of DCT partitions */ - hantro_reg_write(vpu, &vp8_dec_num_dct_partitions, - hdr->num_dct_parts - 1); - - /* DCT partition length */ - hantro_reg_write(vpu, &vp8_dec_stream_len, dct_part_total_len); - - /* DCT partitions base address */ - for (i = 0; i < hdr->num_dct_parts; i++) { - u32 byte_offset = dct_part_offset + dct_size_part_size + count; - u32 base_addr = byte_offset + src_dma; - - hantro_reg_write(vpu, &vp8_dec_dct_base[i], - base_addr & (~DEC_8190_ALIGN_MASK)); - - hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], - (byte_offset & DEC_8190_ALIGN_MASK) * 8); - - count += hdr->dct_part_sizes[i]; - } -} - -/* - * prediction filter taps - * normal 6-tap filters - */ -static void cfg_tap(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - int i, j; - - if ((hdr->version & 0x03) != 0) - return; /* Tap filter not used. */ - - for (i = 0; i < 8; i++) { - for (j = 0; j < 6; j++) { - if (vp8_dec_pred_bc_tap[i][j].base != 0) - hantro_reg_write(vpu, - &vp8_dec_pred_bc_tap[i][j], - hantro_vp8_dec_mc_filter[i][j]); - } - } -} - -static void cfg_ref(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_dev *vpu = ctx->dev; - dma_addr_t ref; - - ref = hantro_get_ref(ctx, hdr->last_frame_ts); - if (!ref) { - vpu_debug(0, "failed to find last frame ts=%llu\n", - hdr->last_frame_ts); - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - } - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF0); - - ref = hantro_get_ref(ctx, hdr->golden_frame_ts); - if (!ref && hdr->golden_frame_ts) - vpu_debug(0, "failed to find golden frame ts=%llu\n", - hdr->golden_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) - ref |= VDPU_REG_VP8_GREF_SIGN_BIAS; - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(2)); - - ref = hantro_get_ref(ctx, hdr->alt_frame_ts); - if (!ref && hdr->alt_frame_ts) - vpu_debug(0, "failed to find alt frame ts=%llu\n", - hdr->alt_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) - ref |= VDPU_REG_VP8_AREF_SIGN_BIAS; - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(3)); -} - -static void cfg_buffers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t dst_dma; - u32 reg; - - /* Set probability table buffer address */ - vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, - VDPU_REG_ADDR_QTABLE); - - /* Set segment map address */ - reg = VDPU_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { - reg |= VDPU_REG_FWD_PIC1_SEGMENT_E; - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) - reg |= VDPU_REG_FWD_PIC1_SEGMENT_UPD_E; - } - vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_SEGMENT_VAL); - - /* set output frame buffer address */ - dst_dma = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - vdpu_write_relaxed(vpu, dst_dma, VDPU_REG_ADDR_DST); -} - -int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp8_frame *hdr; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - size_t height = ctx->dst_fmt.height; - size_t width = ctx->dst_fmt.width; - u32 mb_width, mb_height; - u32 reg; - - hantro_start_prepare_run(ctx); - - hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); - if (WARN_ON(!hdr)) - return -EINVAL; - - /* Reset segment_map buffer in keyframe */ - if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) - memset(ctx->vp8_dec.segment_map.cpu, 0, - ctx->vp8_dec.segment_map.size); - - hantro_vp8_prob_update(ctx, hdr); - - /* - * Extensive testing shows that the hardware does not properly - * clear the internal state from previous a decoding run. This - * causes corruption in decoded frames for multi-instance use cases. - * A soft reset before programming the registers has been found - * to resolve those problems. - */ - ctx->codec_ops->reset(ctx); - - reg = VDPU_REG_CONFIG_DEC_TIMEOUT_E - | VDPU_REG_CONFIG_DEC_CLK_GATE_E; - if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) - reg |= VDPU_REG_DEC_CTRL0_PIC_INTER_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_EN_FLAGS); - - reg = VDPU_REG_CONFIG_DEC_STRENDIAN_E - | VDPU_REG_CONFIG_DEC_INSWAP32_E - | VDPU_REG_CONFIG_DEC_STRSWAP32_E - | VDPU_REG_CONFIG_DEC_OUTSWAP32_E - | VDPU_REG_CONFIG_DEC_IN_ENDIAN - | VDPU_REG_CONFIG_DEC_OUT_ENDIAN; - vdpu_write_relaxed(vpu, reg, VDPU_REG_DATA_ENDIAN); - - reg = VDPU_REG_CONFIG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, VDPU_REG_AXI_CTRL); - - reg = VDPU_REG_DEC_CTRL0_DEC_MODE(10); - vdpu_write_relaxed(vpu, reg, VDPU_REG_DEC_FORMAT); - - if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) - hantro_reg_write(vpu, &vp8_dec_skip_mode, 1); - if (hdr->lf.level == 0) - hantro_reg_write(vpu, &vp8_dec_filter_disable, 1); - - /* Frame dimensions */ - mb_width = MB_WIDTH(width); - mb_height = MB_HEIGHT(height); - - hantro_reg_write(vpu, &vp8_dec_mb_width, mb_width); - hantro_reg_write(vpu, &vp8_dec_mb_height, mb_height); - hantro_reg_write(vpu, &vp8_dec_mb_width_ext, mb_width >> 9); - hantro_reg_write(vpu, &vp8_dec_mb_height_ext, mb_height >> 8); - - /* Boolean decoder */ - hantro_reg_write(vpu, &vp8_dec_bool_range, hdr->coder_state.range); - hantro_reg_write(vpu, &vp8_dec_bool_value, hdr->coder_state.value); - - reg = vdpu_read(vpu, VDPU_REG_VP8_DCT_START_BIT); - if (hdr->version != 3) - reg |= VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT; - if (hdr->version & 0x3) - reg |= VDPU_REG_DEC_CTRL4_BILIN_MC_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_DCT_START_BIT); - - cfg_lf(ctx, hdr); - cfg_qp(ctx, hdr); - cfg_parts(ctx, hdr); - cfg_tap(ctx, hdr); - - vb2_dst = hantro_get_dst_buf(ctx); - cfg_ref(ctx, hdr, vb2_dst); - cfg_buffers(ctx, hdr, vb2_dst); - - hantro_end_prepare_run(ctx); - - hantro_reg_write(vpu, &vp8_dec_start_dec, 1); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_regs.h b/drivers/staging/media/hantro/rockchip_vpu2_regs.h deleted file mode 100644 index 49e40889545b..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_regs.h +++ /dev/null @@ -1,600 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin - */ - -#ifndef ROCKCHIP_VPU2_REGS_H_ -#define ROCKCHIP_VPU2_REGS_H_ - -/* Encoder registers. */ -#define VEPU_REG_VP8_QUT_1ST(i) (0x000 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_DC_Y2(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_2ND(i) (0x004 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_AC_Y1(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_3RD(i) (0x008 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_AC_CHR(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_4TH(i) (0x00c + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_ZB_DC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_QUT_ZB_DC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_QUT_ZB_DC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_QUT_5TH(i) (0x010 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_ZB_AC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_QUT_ZB_AC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_QUT_ZB_AC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_QUT_6TH(i) (0x014 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_RND_DC_CHR(x) (((x) & 0xff) << 16) -#define VEPU_REG_VP8_QUT_RND_DC_Y2(x) (((x) & 0xff) << 8) -#define VEPU_REG_VP8_QUT_RND_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_7TH(i) (0x018 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_RND_AC_CHR(x) (((x) & 0xff) << 16) -#define VEPU_REG_VP8_QUT_RND_AC_Y2(x) (((x) & 0xff) << 8) -#define VEPU_REG_VP8_QUT_RND_AC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_8TH(i) (0x01c + ((i) * 0x24)) -#define VEPU_REG_VP8_SEG_FILTER_LEVEL(x) (((x) & 0x3f) << 25) -#define VEPU_REG_VP8_DEQUT_DC_CHR(x) (((x) & 0xff) << 17) -#define VEPU_REG_VP8_DEQUT_DC_Y2(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_VP8_DEQUT_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_9TH(i) (0x020 + ((i) * 0x24)) -#define VEPU_REG_VP8_DEQUT_AC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_DEQUT_AC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_DEQUT_AC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_ADDR_VP8_SEG_MAP 0x06c -#define VEPU_REG_VP8_INTRA_4X4_PENALTY(i) (0x070 + ((i) * 0x4)) -#define VEPU_REG_VP8_INTRA_4X4_PENALTY_0(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_INTRA_4x4_PENALTY_1(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY(i) (0x084 + ((i) * 0x4)) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY_0(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY_1(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_CONTROL 0x0a0 -#define VEPU_REG_VP8_LF_MODE_DELTA_BPRED(x) (((x) & 0x1f) << 24) -#define VEPU_REG_VP8_LF_REF_DELTA_INTRA_MB(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_INTER_TYPE_BIT_COST(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_REF_FRAME_VAL 0x0a4 -#define VEPU_REG_VP8_COEF_DMV_PENALTY(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_REF_FRAME(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_LOOP_FILTER_REF_DELTA 0x0a8 -#define VEPU_REG_VP8_LF_REF_DELTA_ALT_REF(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_LF_REF_DELTA_LAST_REF(x) (((x) & 0x7f) << 8) -#define VEPU_REG_VP8_LF_REF_DELTA_GOLDEN(x) (((x) & 0x7f) << 0) -#define VEPU_REG_VP8_LOOP_FILTER_MODE_DELTA 0x0ac -#define VEPU_REG_VP8_LF_MODE_DELTA_SPLITMV(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_LF_MODE_DELTA_ZEROMV(x) (((x) & 0x7f) << 8) -#define VEPU_REG_VP8_LF_MODE_DELTA_NEWMV(x) (((x) & 0x7f) << 0) -#define VEPU_REG_JPEG_LUMA_QUAT(i) (0x000 + ((i) * 0x4)) -#define VEPU_REG_JPEG_CHROMA_QUAT(i) (0x040 + ((i) * 0x4)) -#define VEPU_REG_INTRA_SLICE_BITMAP(i) (0x0b0 + ((i) * 0x4)) -#define VEPU_REG_ADDR_VP8_DCT_PART(i) (0x0b0 + ((i) * 0x4)) -#define VEPU_REG_INTRA_AREA_CTRL 0x0b8 -#define VEPU_REG_INTRA_AREA_TOP(x) (((x) & 0xff) << 24) -#define VEPU_REG_INTRA_AREA_BOTTOM(x) (((x) & 0xff) << 16) -#define VEPU_REG_INTRA_AREA_LEFT(x) (((x) & 0xff) << 8) -#define VEPU_REG_INTRA_AREA_RIGHT(x) (((x) & 0xff) << 0) -#define VEPU_REG_CIR_INTRA_CTRL 0x0bc -#define VEPU_REG_CIR_INTRA_FIRST_MB(x) (((x) & 0xffff) << 16) -#define VEPU_REG_CIR_INTRA_INTERVAL(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ADDR_IN_PLANE_0 0x0c0 -#define VEPU_REG_ADDR_IN_PLANE_1 0x0c4 -#define VEPU_REG_ADDR_IN_PLANE_2 0x0c8 -#define VEPU_REG_STR_HDR_REM_MSB 0x0cc -#define VEPU_REG_STR_HDR_REM_LSB 0x0d0 -#define VEPU_REG_STR_BUF_LIMIT 0x0d4 -#define VEPU_REG_AXI_CTRL 0x0d8 -#define VEPU_REG_AXI_CTRL_READ_ID(x) (((x) & 0xff) << 24) -#define VEPU_REG_AXI_CTRL_WRITE_ID(x) (((x) & 0xff) << 16) -#define VEPU_REG_AXI_CTRL_BURST_LEN(x) (((x) & 0x3f) << 8) -#define VEPU_REG_AXI_CTRL_INCREMENT_MODE(x) (((x) & 0x01) << 2) -#define VEPU_REG_AXI_CTRL_BIRST_DISCARD(x) (((x) & 0x01) << 1) -#define VEPU_REG_AXI_CTRL_BIRST_DISABLE BIT(0) -#define VEPU_QP_ADJUST_MAD_DELTA_ROI 0x0dc -#define VEPU_REG_ROI_QP_DELTA_1 (((x) & 0xf) << 12) -#define VEPU_REG_ROI_QP_DELTA_2 (((x) & 0xf) << 8) -#define VEPU_REG_MAD_QP_ADJUSTMENT (((x) & 0xf) << 0) -#define VEPU_REG_ADDR_REF_LUMA 0x0e0 -#define VEPU_REG_ADDR_REF_CHROMA 0x0e4 -#define VEPU_REG_QP_SUM_DIV2 0x0e8 -#define VEPU_REG_QP_SUM(x) (((x) & 0x001fffff) * 2) -#define VEPU_REG_ENC_CTRL0 0x0ec -#define VEPU_REG_DISABLE_QUARTER_PIXEL_MV BIT(28) -#define VEPU_REG_DEBLOCKING_FILTER_MODE(x) (((x) & 0x3) << 24) -#define VEPU_REG_CABAC_INIT_IDC(x) (((x) & 0x3) << 21) -#define VEPU_REG_ENTROPY_CODING_MODE BIT(20) -#define VEPU_REG_H264_TRANS8X8_MODE BIT(17) -#define VEPU_REG_H264_INTER4X4_MODE BIT(16) -#define VEPU_REG_H264_STREAM_MODE BIT(15) -#define VEPU_REG_H264_SLICE_SIZE(x) (((x) & 0x7f) << 8) -#define VEPU_REG_ENC_OVER_FILL_STRM_OFFSET 0x0f0 -#define VEPU_REG_STREAM_START_OFFSET(x) (((x) & 0x3f) << 16) -#define VEPU_REG_SKIP_MACROBLOCK_PENALTY(x) (((x) & 0xff) << 8) -#define VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(x) (((x) & 0x3) << 4) -#define VEPU_REG_IN_IMG_CTRL_OVRFLB(x) (((x) & 0xf) << 0) -#define VEPU_REG_INPUT_LUMA_INFO 0x0f4 -#define VEPU_REG_IN_IMG_CHROMA_OFFSET(x) (((x) & 0x7) << 20) -#define VEPU_REG_IN_IMG_LUMA_OFFSET(x) (((x) & 0x7) << 16) -#define VEPU_REG_IN_IMG_CTRL_ROW_LEN(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_RLC_SUM 0x0f8 -#define VEPU_REG_RLC_SUM_OUT(x) (((x) & 0x007fffff) * 4) -#define VEPU_REG_SPLIT_PENALTY_4X4 0x0f8 -#define VEPU_REG_VP8_SPLIT_PENALTY_4X4 (((x) & 0x1ff) << 19) -#define VEPU_REG_ADDR_REC_LUMA 0x0fc -#define VEPU_REG_ADDR_REC_CHROMA 0x100 -#define VEPU_REG_CHECKPOINT(i) (0x104 + ((i) * 0x4)) -#define VEPU_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) -#define VEPU_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) -#define VEPU_REG_CHECKPOINT_RESULT(x) \ - ((((x) >> (16 - 16 * ((i) & 1))) & 0xffff) * 32) -#define VEPU_REG_VP8_SEG0_QUANT_AC_Y1 0x104 -#define VEPU_REG_VP8_SEG0_RND_AC_Y1(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y1(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_Y2 0x108 -#define VEPU_REG_VP8_SEG0_RND_DC_Y2(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y2(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_AC_Y2 0x10c -#define VEPU_REG_VP8_SEG0_RND_AC_Y2(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y2(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_CHR 0x110 -#define VEPU_REG_VP8_SEG0_RND_DC_CHR(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_CHR(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_AC_CHR 0x114 -#define VEPU_REG_VP8_SEG0_RND_AC_CHR(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_CHR(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DQUT 0x118 -#define VEPU_REG_VP8_MV_REF_IDX1(x) (((x) & 0x03) << 26) -#define VEPU_REG_VP8_SEG0_DQUT_DC_Y2(x) (((x) & 0x1ff) << 17) -#define VEPU_REG_VP8_SEG0_DQUT_AC_Y1(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_VP8_SEG0_DQUT_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_CHKPT_WORD_ERR(i) (0x118 + ((i) * 0x4)) -#define VEPU_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) -#define VEPU_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) -#define VEPU_REG_VP8_SEG0_QUANT_DQUT_1 0x11c -#define VEPU_REG_VP8_SEGMENT_MAP_UPDATE BIT(30) -#define VEPU_REG_VP8_SEGMENT_EN BIT(29) -#define VEPU_REG_VP8_MV_REF_IDX2_EN BIT(28) -#define VEPU_REG_VP8_MV_REF_IDX2(x) (((x) & 0x03) << 26) -#define VEPU_REG_VP8_SEG0_DQUT_AC_CHR(x) (((x) & 0x1ff) << 17) -#define VEPU_REG_VP8_SEG0_DQUT_DC_CHR(x) (((x) & 0xff) << 9) -#define VEPU_REG_VP8_SEG0_DQUT_AC_Y2(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_BOOL_ENC_VALUE 0x120 -#define VEPU_REG_CHKPT_DELTA_QP 0x124 -#define VEPU_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) -#define VEPU_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) -#define VEPU_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) -#define VEPU_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) -#define VEPU_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) -#define VEPU_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) -#define VEPU_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) -#define VEPU_REG_VP8_ENC_CTRL2 0x124 -#define VEPU_REG_VP8_ZERO_MV_PENALTY_FOR_REF2(x) (((x) & 0xff) << 24) -#define VEPU_REG_VP8_FILTER_SHARPNESS(x) (((x) & 0x07) << 21) -#define VEPU_REG_VP8_FILTER_LEVEL(x) (((x) & 0x3f) << 15) -#define VEPU_REG_VP8_DCT_PARTITION_CNT(x) (((x) & 0x03) << 13) -#define VEPU_REG_VP8_BOOL_ENC_VALUE_BITS(x) (((x) & 0x1f) << 8) -#define VEPU_REG_VP8_BOOL_ENC_RANGE(x) (((x) & 0xff) << 0) -#define VEPU_REG_ENC_CTRL1 0x128 -#define VEPU_REG_MAD_THRESHOLD(x) (((x) & 0x3f) << 24) -#define VEPU_REG_COMPLETED_SLICES(x) (((x) & 0xff) << 16) -#define VEPU_REG_IN_IMG_CTRL_FMT(x) (((x) & 0xf) << 4) -#define VEPU_REG_IN_IMG_ROTATE_MODE(x) (((x) & 0x3) << 2) -#define VEPU_REG_SIZE_TABLE_PRESENT BIT(0) -#define VEPU_REG_INTRA_INTER_MODE 0x12c -#define VEPU_REG_INTRA16X16_MODE(x) (((x) & 0xffff) << 16) -#define VEPU_REG_INTER_MODE(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ENC_CTRL2 0x130 -#define VEPU_REG_PPS_INIT_QP(x) (((x) & 0x3f) << 26) -#define VEPU_REG_SLICE_FILTER_ALPHA(x) (((x) & 0xf) << 22) -#define VEPU_REG_SLICE_FILTER_BETA(x) (((x) & 0xf) << 18) -#define VEPU_REG_CHROMA_QP_OFFSET(x) (((x) & 0x1f) << 13) -#define VEPU_REG_FILTER_DISABLE BIT(5) -#define VEPU_REG_IDR_PIC_ID(x) (((x) & 0xf) << 1) -#define VEPU_REG_CONSTRAINED_INTRA_PREDICTION BIT(0) -#define VEPU_REG_ADDR_OUTPUT_STREAM 0x134 -#define VEPU_REG_ADDR_OUTPUT_CTRL 0x138 -#define VEPU_REG_ADDR_NEXT_PIC 0x13c -#define VEPU_REG_ADDR_MV_OUT 0x140 -#define VEPU_REG_ADDR_CABAC_TBL 0x144 -#define VEPU_REG_ROI1 0x148 -#define VEPU_REG_ROI1_TOP_MB(x) (((x) & 0xff) << 24) -#define VEPU_REG_ROI1_BOTTOM_MB(x) (((x) & 0xff) << 16) -#define VEPU_REG_ROI1_LEFT_MB(x) (((x) & 0xff) << 8) -#define VEPU_REG_ROI1_RIGHT_MB(x) (((x) & 0xff) << 0) -#define VEPU_REG_ROI2 0x14c -#define VEPU_REG_ROI2_TOP_MB(x) (((x) & 0xff) << 24) -#define VEPU_REG_ROI2_BOTTOM_MB(x) (((x) & 0xff) << 16) -#define VEPU_REG_ROI2_LEFT_MB(x) (((x) & 0xff) << 8) -#define VEPU_REG_ROI2_RIGHT_MB(x) (((x) & 0xff) << 0) -#define VEPU_REG_STABLE_MATRIX(i) (0x150 + ((i) * 0x4)) -#define VEPU_REG_STABLE_MOTION_SUM 0x174 -#define VEPU_REG_STABILIZATION_OUTPUT 0x178 -#define VEPU_REG_STABLE_MIN_VALUE(x) (((x) & 0xffffff) << 8) -#define VEPU_REG_STABLE_MODE_SEL(x) (((x) & 0x3) << 6) -#define VEPU_REG_STABLE_HOR_GMV(x) (((x) & 0x3f) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF1 0x17c -#define VEPU_REG_RGB2YUV_CONVERSION_COEFB(x) (((x) & 0xffff) << 16) -#define VEPU_REG_RGB2YUV_CONVERSION_COEFA(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF2 0x180 -#define VEPU_REG_RGB2YUV_CONVERSION_COEFE(x) (((x) & 0xffff) << 16) -#define VEPU_REG_RGB2YUV_CONVERSION_COEFC(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF3 0x184 -#define VEPU_REG_RGB2YUV_CONVERSION_COEFF(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB_MASK_MSB 0x188 -#define VEPU_REG_RGB_MASK_B_MSB(x) (((x) & 0x1f) << 16) -#define VEPU_REG_RGB_MASK_G_MSB(x) (((x) & 0x1f) << 8) -#define VEPU_REG_RGB_MASK_R_MSB(x) (((x) & 0x1f) << 0) -#define VEPU_REG_MV_PENALTY 0x18c -#define VEPU_REG_1MV_PENALTY(x) (((x) & 0x3ff) << 21) -#define VEPU_REG_QMV_PENALTY(x) (((x) & 0x3ff) << 11) -#define VEPU_REG_4MV_PENALTY(x) (((x) & 0x3ff) << 1) -#define VEPU_REG_SPLIT_MV_MODE_EN BIT(0) -#define VEPU_REG_QP_VAL 0x190 -#define VEPU_REG_H264_LUMA_INIT_QP(x) (((x) & 0x3f) << 26) -#define VEPU_REG_H264_QP_MAX(x) (((x) & 0x3f) << 20) -#define VEPU_REG_H264_QP_MIN(x) (((x) & 0x3f) << 14) -#define VEPU_REG_H264_CHKPT_DISTANCE(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_Y1 0x190 -#define VEPU_REG_VP8_SEG0_RND_DC_Y1(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y1(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_MVC_RELATE 0x198 -#define VEPU_REG_ZERO_MV_FAVOR_D2(x) (((x) & 0xf) << 20) -#define VEPU_REG_PENALTY_4X4MV(x) (((x) & 0x1ff) << 11) -#define VEPU_REG_MVC_VIEW_ID(x) (((x) & 0x7) << 8) -#define VEPU_REG_MVC_ANCHOR_PIC_FLAG BIT(7) -#define VEPU_REG_MVC_PRIORITY_ID(x) (((x) & 0x7) << 4) -#define VEPU_REG_MVC_TEMPORAL_ID(x) (((x) & 0x7) << 1) -#define VEPU_REG_MVC_INTER_VIEW_FLAG BIT(0) -#define VEPU_REG_ENCODE_START 0x19c -#define VEPU_REG_MB_HEIGHT(x) (((x) & 0x1ff) << 20) -#define VEPU_REG_MB_WIDTH(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_FRAME_TYPE_INTER (0x0 << 6) -#define VEPU_REG_FRAME_TYPE_INTRA (0x1 << 6) -#define VEPU_REG_FRAME_TYPE_MVCINTER (0x2 << 6) -#define VEPU_REG_ENCODE_FORMAT_JPEG (0x2 << 4) -#define VEPU_REG_ENCODE_FORMAT_H264 (0x3 << 4) -#define VEPU_REG_ENCODE_ENABLE BIT(0) -#define VEPU_REG_MB_CTRL 0x1a0 -#define VEPU_REG_MB_CNT_OUT(x) (((x) & 0xffff) << 16) -#define VEPU_REG_MB_CNT_SET(x) (((x) & 0xffff) << 0) -#define VEPU_REG_DATA_ENDIAN 0x1a4 -#define VEPU_REG_INPUT_SWAP8 BIT(31) -#define VEPU_REG_INPUT_SWAP16 BIT(30) -#define VEPU_REG_INPUT_SWAP32 BIT(29) -#define VEPU_REG_OUTPUT_SWAP8 BIT(28) -#define VEPU_REG_OUTPUT_SWAP16 BIT(27) -#define VEPU_REG_OUTPUT_SWAP32 BIT(26) -#define VEPU_REG_TEST_IRQ BIT(24) -#define VEPU_REG_TEST_COUNTER(x) (((x) & 0xf) << 20) -#define VEPU_REG_TEST_REG BIT(19) -#define VEPU_REG_TEST_MEMORY BIT(18) -#define VEPU_REG_TEST_LEN(x) (((x) & 0x3ffff) << 0) -#define VEPU_REG_ENC_CTRL3 0x1a8 -#define VEPU_REG_PPS_ID(x) (((x) & 0xff) << 24) -#define VEPU_REG_INTRA_PRED_MODE(x) (((x) & 0xff) << 16) -#define VEPU_REG_FRAME_NUM(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ENC_CTRL4 0x1ac -#define VEPU_REG_MV_PENALTY_16X8_8X16(x) (((x) & 0x3ff) << 20) -#define VEPU_REG_MV_PENALTY_8X8(x) (((x) & 0x3ff) << 10) -#define VEPU_REG_MV_PENALTY_8X4_4X8(x) (((x) & 0x3ff) << 0) -#define VEPU_REG_ADDR_VP8_PROB_CNT 0x1b0 -#define VEPU_REG_INTERRUPT 0x1b4 -#define VEPU_REG_INTERRUPT_NON BIT(28) -#define VEPU_REG_MV_WRITE_EN BIT(24) -#define VEPU_REG_RECON_WRITE_DIS BIT(20) -#define VEPU_REG_INTERRUPT_SLICE_READY_EN BIT(16) -#define VEPU_REG_CLK_GATING_EN BIT(12) -#define VEPU_REG_INTERRUPT_TIMEOUT_EN BIT(10) -#define VEPU_REG_INTERRUPT_RESET BIT(9) -#define VEPU_REG_INTERRUPT_DIS_BIT BIT(8) -#define VEPU_REG_INTERRUPT_TIMEOUT BIT(6) -#define VEPU_REG_INTERRUPT_BUFFER_FULL BIT(5) -#define VEPU_REG_INTERRUPT_BUS_ERROR BIT(4) -#define VEPU_REG_INTERRUPT_FUSE BIT(3) -#define VEPU_REG_INTERRUPT_SLICE_READY BIT(2) -#define VEPU_REG_INTERRUPT_FRAME_READY BIT(1) -#define VEPU_REG_INTERRUPT_BIT BIT(0) -#define VEPU_REG_DMV_PENALTY_TBL(i) (0x1E0 + ((i) * 0x4)) -#define VEPU_REG_DMV_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) -#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TBL(i) (0x260 + ((i) * 0x4)) -#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) - -/* vpu decoder register */ -#define VDPU_REG_DEC_CTRL0 0x0c8 // 50 -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 25) -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 13) -#define VDPU_REG_CONFIG_TILED_MODE_LSB BIT(12) -#define VDPU_REG_CONFIG_DEC_ADV_PRE_DIS BIT(11) -#define VDPU_REG_CONFIG_DEC_SCMD_DIS BIT(10) -#define VDPU_REG_DEC_CTRL0_SKIP_MODE BIT(9) -#define VDPU_REG_DEC_CTRL0_FILTERING_DIS BIT(8) -#define VDPU_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(7) -#define VDPU_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 1) -#define VDPU_REG_CONFIG_TILED_MODE_MSB(x) BIT(0) -#define VDPU_REG_CONFIG_DEC_OUT_TILED_E BIT(0) -#define VDPU_REG_STREAM_LEN 0x0cc -#define VDPU_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) -#define VDPU_REG_DEC_STREAM_LEN_HI BIT(24) -#define VDPU_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) -#define VDPU_REG_ERROR_CONCEALMENT 0x0d0 -#define VDPU_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 17) -#define VDPU_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 8) -#define VDPU_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 0) -#define VDPU_REG_DEC_FORMAT 0x0d4 -#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) -#define VDPU_REG_DATA_ENDIAN 0x0d8 -#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) -#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) -#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) -#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) -#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) -#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) -#define VDPU_REG_INTERRUPT 0x0dc -#define VDPU_REG_INTERRUPT_DEC_TIMEOUT BIT(13) -#define VDPU_REG_INTERRUPT_DEC_ERROR_INT BIT(12) -#define VDPU_REG_INTERRUPT_DEC_PIC_INF BIT(10) -#define VDPU_REG_INTERRUPT_DEC_SLICE_INT BIT(9) -#define VDPU_REG_INTERRUPT_DEC_ASO_INT BIT(8) -#define VDPU_REG_INTERRUPT_DEC_BUFFER_INT BIT(6) -#define VDPU_REG_INTERRUPT_DEC_BUS_INT BIT(5) -#define VDPU_REG_INTERRUPT_DEC_RDY_INT BIT(4) -#define VDPU_REG_INTERRUPT_DEC_IRQ_DIS BIT(1) -#define VDPU_REG_INTERRUPT_DEC_IRQ BIT(0) -#define VDPU_REG_AXI_CTRL 0x0e0 -#define VDPU_REG_AXI_DEC_SEL BIT(23) -#define VDPU_REG_CONFIG_DEC_DATA_DISC_E BIT(22) -#define VDPU_REG_PARAL_BUS_E(x) BIT(21) -#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) -#define VDPU_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 8) -#define VDPU_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 0) -#define VDPU_REG_EN_FLAGS 0x0e4 -#define VDPU_REG_AHB_HLOCK_E BIT(31) -#define VDPU_REG_CACHE_E BIT(29) -#define VDPU_REG_PREFETCH_SINGLE_CHANNEL_E BIT(28) -#define VDPU_REG_INTRA_3_CYCLE_ENHANCE BIT(27) -#define VDPU_REG_INTRA_DOUBLE_SPEED BIT(26) -#define VDPU_REG_INTER_DOUBLE_SPEED BIT(25) -#define VDPU_REG_DEC_CTRL3_START_CODE_E BIT(22) -#define VDPU_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(21) -#define VDPU_REG_DEC_CTRL0_RLC_MODE_E BIT(20) -#define VDPU_REG_DEC_CTRL0_DIVX3_E BIT(19) -#define VDPU_REG_DEC_CTRL0_PJPEG_E BIT(18) -#define VDPU_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(17) -#define VDPU_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(16) -#define VDPU_REG_DEC_CTRL0_PIC_B_E BIT(15) -#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) -#define VDPU_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(13) -#define VDPU_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(12) -#define VDPU_REG_DEC_CTRL0_SORENSON_E BIT(11) -#define VDPU_REG_DEC_CTRL0_WRITE_MVS_E BIT(10) -#define VDPU_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(9) -#define VDPU_REG_DEC_CTRL0_REFTOPFIRST_E BIT(8) -#define VDPU_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(7) -#define VDPU_REG_DEC_CTRL0_PICORD_COUNT_E BIT(6) -#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) -#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) -#define VDPU_REG_DEC_CTRL0_DEC_OUT_DIS BIT(2) -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(1) -#define VDPU_REG_INTERRUPT_DEC_E BIT(0) -#define VDPU_REG_SOFT_RESET 0x0e8 -#define VDPU_REG_PRED_FLT 0x0ec -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_ADDITIONAL_CHROMA_ADDRESS 0x0f0 -#define VDPU_REG_ADDR_QTABLE 0x0f4 -#define VDPU_REG_DIRECT_MV_ADDR 0x0f8 -#define VDPU_REG_ADDR_DST 0x0fc -#define VDPU_REG_ADDR_STR 0x100 -#define VDPU_REG_REFBUF_RELATED 0x104 -#define VDPU_REG_FWD_PIC(i) (0x128 + ((i) * 0x4)) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_REF_PIC(i) (0x130 + ((i) * 0x4)) -#define VDPU_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) -#define VDPU_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) -#define VDPU_REG_H264_ADDR_REF(i) (0x150 + ((i) * 0x4)) -#define VDPU_REG_ADDR_REF_FIELD_E BIT(1) -#define VDPU_REG_ADDR_REF_TOPC_E BIT(0) -#define VDPU_REG_INITIAL_REF_PIC_LIST0 0x190 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST1 0x194 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F11(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F10(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F9(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F8(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F7(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F6(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST2 0x198 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F14(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F13(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F12(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST3 0x19c -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST4 0x1a0 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B11(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B10(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B9(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B8(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B7(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B6(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST5 0x1a4 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B14(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B13(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B12(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST6 0x1a8 -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_LT_REF 0x1ac -#define VDPU_REG_VALID_REF 0x1b0 -#define VDPU_REG_H264_PIC_MB_SIZE 0x1b8 -#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 22) -#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 17) -#define VDPU_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 9) -#define VDPU_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 0) -#define VDPU_REG_H264_CTRL 0x1bc -#define VDPU_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 16) -#define VDPU_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) -#define VDPU_REG_CURRENT_FRAME 0x1c0 -#define VDPU_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(31) -#define VDPU_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(30) -#define VDPU_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) -#define VDPU_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) -#define VDPU_REG_REF_FRAME 0x1c4 -#define VDPU_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 16) -#define VDPU_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) -#define VDPU_REG_DEC_CTRL6 0x1c8 -#define VDPU_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) -#define VDPU_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) -#define VDPU_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) -#define VDPU_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) -#define VDPU_REG_ENABLE_FLAG 0x1cc -#define VDPU_REG_DEC_CTRL5_IDR_PIC_E BIT(8) -#define VDPU_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(7) -#define VDPU_REG_DEC_CTRL4_BLACKWHITE_E BIT(6) -#define VDPU_REG_DEC_CTRL4_CABAC_E BIT(5) -#define VDPU_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(4) -#define VDPU_REG_DEC_CTRL5_CONST_INTRA_E BIT(3) -#define VDPU_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(2) -#define VDPU_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(1) -#define VDPU_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) -#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 -#define VDPU_REG_DEC_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) -#define VDPU_REG_DEC_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) -#define VDPU_REG_DEC_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) -#define VDPU_REG_DEC_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) -#define VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) -#define VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) -#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 -#define VDPU_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) -#define VDPU_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) -#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define VDPU_REG_VP8_CTRL0 0x1e8 -#define VDPU_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) -#define VDPU_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) -#define VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) -#define VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) -#define VDPU_REG_VP8_DATA_VAL 0x1f0 -#define VDPU_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) -#define VDPU_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) -#define VDPU_REG_PRED_FLT7 0x1f4 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT8 0x1f8 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT9 0x1fc -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT10 0x200 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) -#define VDPU_REG_FILTER_LEVEL 0x204 -#define VDPU_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) -#define VDPU_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) -#define VDPU_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) -#define VDPU_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) -#define VDPU_REG_VP8_QUANTER0 0x208 -#define VDPU_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) -#define VDPU_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_VP8_ADDR_REF0 0x20c -#define VDPU_REG_FILTER_MB_ADJ 0x210 -#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define VDPU_REG_FILT_MB_ADJ_0(x) (((x) & 0x7f) << 21) -#define VDPU_REG_FILT_MB_ADJ_1(x) (((x) & 0x7f) << 14) -#define VDPU_REG_FILT_MB_ADJ_2(x) (((x) & 0x7f) << 7) -#define VDPU_REG_FILT_MB_ADJ_3(x) (((x) & 0x7f) << 0) -#define VDPU_REG_FILTER_REF_ADJ 0x214 -#define VDPU_REG_REF_PIC_ADJ_0(x) (((x) & 0x7f) << 21) -#define VDPU_REG_REF_PIC_ADJ_1(x) (((x) & 0x7f) << 14) -#define VDPU_REG_REF_PIC_ADJ_2(x) (((x) & 0x7f) << 7) -#define VDPU_REG_REF_PIC_ADJ_3(x) (((x) & 0x7f) << 0) -#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) -#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_DCT_BASE(i) (0x230 + ((i) * 0x4)) -#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 -#define VDPU_REG_VP8_ADDR_REF1 0x250 -#define VDPU_REG_VP8_SEGMENT_VAL 0x254 -#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define VDPU_REG_VP8_DCT_START_BIT2 0x258 -#define VDPU_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) -#define VDPU_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) -#define VDPU_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) -#define VDPU_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) -#define VDPU_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) -#define VDPU_REG_VP8_QUANTER1 0x25c -#define VDPU_REG_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) -#define VDPU_REG_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_VP8_QUANTER2 0x260 -#define VDPU_REG_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_4(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_5(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_PRED_FLT1 0x264 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT2 0x268 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT3 0x26c -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT4 0x270 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT5 0x274 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT6 0x278 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0(x) (((x) & 0x3ff) << 2) - -#endif /* ROCKCHIP_VPU2_REGS_H_ */ diff --git a/drivers/staging/media/hantro/rockchip_vpu_hw.c b/drivers/staging/media/hantro/rockchip_vpu_hw.c deleted file mode 100644 index 8de6fd2e8eef..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu_hw.c +++ /dev/null @@ -1,680 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Jeffy Chen - */ - -#include - -#include "hantro.h" -#include "hantro_jpeg.h" -#include "hantro_g1_regs.h" -#include "hantro_h1_regs.h" -#include "rockchip_vpu2_regs.h" - -#define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000) -#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000) - -/* - * Supported formats. - */ - -static const struct hantro_fmt rockchip_vpu_enc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUV420M, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420P, - }, - { - .fourcc = V4L2_PIX_FMT_NV12M, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420SP, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUYV422, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_UYVY422, - }, - { - .fourcc = V4L2_PIX_FMT_JPEG, - .codec_mode = HANTRO_MODE_JPEG_ENC, - .max_depth = 2, - .header_size = JPEG_HEADER_SIZE, - .frmsize = { - .min_width = 96, - .max_width = 8192, - .step_width = MB_DIM, - .min_height = 32, - .max_height = 8192, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rockchip_vpu1_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3066_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3288_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_4K_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_4K_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_4K_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_4K_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rockchip_vdpu2_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3399_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static irqreturn_t rockchip_vpu1_vepu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vepu_read(vpu, H1_REG_INTERRUPT); - state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vepu_write(vpu, 0, H1_REG_INTERRUPT); - vepu_write(vpu, 0, H1_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_vpu2_vdpu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, VDPU_REG_INTERRUPT); - state = (status & VDPU_REG_INTERRUPT_DEC_IRQ) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); - vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_vpu2_vepu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vepu_read(vpu, VEPU_REG_INTERRUPT); - state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vepu_write(vpu, 0, VEPU_REG_INTERRUPT); - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static int rk3036_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLK to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); - return 0; -} - -static int rk3066_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLKs to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); - clk_set_rate(vpu->clocks[2].clk, RK3066_ACLK_MAX_FREQ); - return 0; -} - -static int rockchip_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLK to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3288_ACLK_MAX_FREQ); - return 0; -} - -static void rk3066_vpu_dec_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); -} - -static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vepu_write(vpu, H1_REG_INTERRUPT_DIS_BIT, H1_REG_INTERRUPT); - vepu_write(vpu, 0, H1_REG_ENC_CTRL); - vepu_write(vpu, 0, H1_REG_AXI_CTRL); -} - -static void rockchip_vpu2_dec_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); - vdpu_write(vpu, 0, VDPU_REG_EN_FLAGS); - vdpu_write(vpu, 1, VDPU_REG_SOFT_RESET); -} - -static void rockchip_vpu2_enc_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT); - vepu_write(vpu, 0, VEPU_REG_ENCODE_START); - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); -} - -/* - * Supported codec ops. - */ -static const struct hantro_codec_ops rk3036_vpu_codec_ops[] = { - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3066_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = hantro_h1_jpeg_enc_run, - .reset = rockchip_vpu1_enc_reset, - .done = hantro_h1_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = hantro_h1_jpeg_enc_run, - .reset = rockchip_vpu1_enc_reset, - .done = hantro_h1_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3399_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = rockchip_vpu2_jpeg_enc_run, - .reset = rockchip_vpu2_enc_reset, - .done = rockchip_vpu2_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = rockchip_vpu2_h264_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = rockchip_vpu2_mpeg2_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = rockchip_vpu2_vp8_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3568_vepu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = rockchip_vpu2_jpeg_enc_run, - .reset = rockchip_vpu2_enc_reset, - .done = rockchip_vpu2_jpeg_enc_done, - }, -}; - -/* - * VPU variant. - */ - -static const struct hantro_irq rockchip_vdpu1_irqs[] = { - { "vdpu", hantro_g1_irq }, -}; - -static const struct hantro_irq rockchip_vpu1_irqs[] = { - { "vepu", rockchip_vpu1_vepu_irq }, - { "vdpu", hantro_g1_irq }, -}; - -static const struct hantro_irq rockchip_vdpu2_irqs[] = { - { "vdpu", rockchip_vpu2_vdpu_irq }, -}; - -static const struct hantro_irq rockchip_vpu2_irqs[] = { - { "vepu", rockchip_vpu2_vepu_irq }, - { "vdpu", rockchip_vpu2_vdpu_irq }, -}; - -static const struct hantro_irq rk3568_vepu_irqs[] = { - { "vepu", rockchip_vpu2_vepu_irq }, -}; - -static const char * const rk3066_vpu_clk_names[] = { - "aclk_vdpu", "hclk_vdpu", - "aclk_vepu", "hclk_vepu" -}; - -static const char * const rockchip_vpu_clk_names[] = { - "aclk", "hclk" -}; - -/* VDPU1/VEPU1 */ - -const struct hantro_variant rk3036_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rk3066_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = rk3036_vpu_codec_ops, - .irqs = rockchip_vdpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu1_irqs), - .init = rk3036_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -/* - * Despite this variant has separate clocks for decoder and encoder, - * it's still required to enable all four of them for either decoding - * or encoding and we can't split it in separate g1/h1 variants. - */ -const struct hantro_variant rk3066_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3066_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3066_vpu_codec_ops, - .irqs = rockchip_vpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), - .init = rk3066_vpu_hw_init, - .clk_names = rk3066_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rk3066_vpu_clk_names) -}; - -const struct hantro_variant rk3288_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3288_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3288_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3288_vpu_codec_ops, - .irqs = rockchip_vpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -/* VDPU2/VEPU2 */ - -const struct hantro_variant rk3328_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vdpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names), -}; - -/* - * H.264 decoding explicitly disabled in RK3399. - * This ensures userspace applications use the Rockchip VDEC core, - * which has better performance. - */ -const struct hantro_variant rk3399_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3399_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts), - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant rk3568_vepu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .codec = HANTRO_JPEG_ENCODER, - .codec_ops = rk3568_vepu_codec_ops, - .irqs = rk3568_vepu_irqs, - .num_irqs = ARRAY_SIZE(rk3568_vepu_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant rk3568_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vdpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant px30_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), - .init = rk3036_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; diff --git a/drivers/staging/media/hantro/sama5d4_vdec_hw.c b/drivers/staging/media/hantro/sama5d4_vdec_hw.c deleted file mode 100644 index b205e2db5b04..000000000000 --- a/drivers/staging/media/hantro/sama5d4_vdec_hw.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VDEC driver - * - * Copyright (C) 2021 Collabora Ltd, Emil Velikov - */ - -#include "hantro.h" - -/* - * Supported formats. - */ - -static const struct hantro_fmt sama5d4_vdec_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt sama5d4_vdec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -/* - * Supported codec ops. - */ - -static const struct hantro_codec_ops sama5d4_vdec_codec_ops[] = { - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, -}; - -static const struct hantro_irq sama5d4_irqs[] = { - { "vdec", hantro_g1_irq }, -}; - -static const char * const sama5d4_clk_names[] = { "vdec_clk" }; - -const struct hantro_variant sama5d4_vdec_variant = { - .dec_fmts = sama5d4_vdec_fmts, - .num_dec_fmts = ARRAY_SIZE(sama5d4_vdec_fmts), - .postproc_fmts = sama5d4_vdec_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(sama5d4_vdec_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = sama5d4_vdec_codec_ops, - .irqs = sama5d4_irqs, - .num_irqs = ARRAY_SIZE(sama5d4_irqs), - .clk_names = sama5d4_clk_names, - .num_clocks = ARRAY_SIZE(sama5d4_clk_names), -}; diff --git a/drivers/staging/media/hantro/sunxi_vpu_hw.c b/drivers/staging/media/hantro/sunxi_vpu_hw.c deleted file mode 100644 index 02ce8b064a8f..000000000000 --- a/drivers/staging/media/hantro/sunxi_vpu_hw.c +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Allwinner Hantro G2 VPU codec driver - * - * Copyright (C) 2021 Jernej Skrabec - */ - -#include - -#include "hantro.h" - -static const struct hantro_fmt sunxi_vpu_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_P010, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, -}; - -static const struct hantro_fmt sunxi_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12_4L4, - .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_P010_4L4, - .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9_FRAME, - .codec_mode = HANTRO_MODE_VP9_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, -}; - -static int sunxi_vpu_hw_init(struct hantro_dev *vpu) -{ - clk_set_rate(vpu->clocks[0].clk, 300000000); - - return 0; -} - -static void sunxi_vpu_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - reset_control_reset(vpu->resets); -} - -static const struct hantro_codec_ops sunxi_vpu_codec_ops[] = { - [HANTRO_MODE_VP9_DEC] = { - .run = hantro_g2_vp9_dec_run, - .done = hantro_g2_vp9_dec_done, - .reset = sunxi_vpu_reset, - .init = hantro_vp9_dec_init, - .exit = hantro_vp9_dec_exit, - }, -}; - -static const struct hantro_irq sunxi_irqs[] = { - { NULL, hantro_g2_irq }, -}; - -static const char * const sunxi_clk_names[] = { "mod", "bus" }; - -const struct hantro_variant sunxi_vpu_variant = { - .dec_fmts = sunxi_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(sunxi_vpu_dec_fmts), - .postproc_fmts = sunxi_vpu_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(sunxi_vpu_postproc_fmts), - .postproc_ops = &hantro_g2_postproc_ops, - .codec = HANTRO_VP9_DECODER, - .codec_ops = sunxi_vpu_codec_ops, - .init = sunxi_vpu_hw_init, - .irqs = sunxi_irqs, - .num_irqs = ARRAY_SIZE(sunxi_irqs), - .clk_names = sunxi_clk_names, - .num_clocks = ARRAY_SIZE(sunxi_clk_names), - .double_buffer = 1, - .legacy_regs = 1, - .late_postproc = 1, -}; -- cgit v1.3-14-g43fede