blob: abeae8ace0d551188f98dc4cd7f8d98bbd8ef2b5 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*/
#include "cam_subdev.h"
#include "cam_node.h"
#include "cam_debug_util.h"
/**
* cam_subdev_subscribe_event()
*
* @brief: function to subscribe to v4l2 events
*
* @sd: Pointer to struct v4l2_subdev.
* @fh: Pointer to struct v4l2_fh.
* @sub: Pointer to struct v4l2_event_subscription.
*/
static int cam_subdev_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
return v4l2_event_subscribe(fh, sub, CAM_SUBDEVICE_EVENT_MAX, NULL);
}
/**
* cam_subdev_unsubscribe_event()
*
* @brief: function to unsubscribe from v4l2 events
*
* @sd: Pointer to struct v4l2_subdev.
* @fh: Pointer to struct v4l2_fh.
* @sub: Pointer to struct v4l2_event_subscription.
*/
static int cam_subdev_unsubscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
return v4l2_event_unsubscribe(fh, sub);
}
static long cam_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
void *arg)
{
long rc;
struct cam_node *node =
(struct cam_node *) v4l2_get_subdevdata(sd);
if (!node || node->state == CAM_NODE_STATE_UNINIT) {
rc = -EINVAL;
goto end;
}
switch (cmd) {
case VIDIOC_CAM_CONTROL:
rc = cam_node_handle_ioctl(node,
(struct cam_control *) arg);
break;
default:
CAM_ERR(CAM_CORE, "Invalid command %d for %s", cmd,
node->name);
rc = -EINVAL;
}
end:
return rc;
}
#ifdef CONFIG_COMPAT
static long cam_subdev_compat_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
struct cam_control cmd_data;
int rc;
if (copy_from_user(&cmd_data, (void __user *)arg,
sizeof(cmd_data))) {
CAM_ERR(CAM_CORE, "Failed to copy from user_ptr=%pK size=%zu",
(void __user *)arg, sizeof(cmd_data));
return -EFAULT;
}
rc = cam_subdev_ioctl(sd, cmd, &cmd_data);
if (!rc) {
if (copy_to_user((void __user *)arg, &cmd_data,
sizeof(cmd_data))) {
CAM_ERR(CAM_CORE,
"Failed to copy to user_ptr=%pK size=%zu",
(void __user *)arg, sizeof(cmd_data));
rc = -EFAULT;
}
}
return rc;
}
#endif
const struct v4l2_subdev_core_ops cam_subdev_core_ops = {
.ioctl = cam_subdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = cam_subdev_compat_ioctl,
#endif
.subscribe_event = cam_subdev_subscribe_event,
.unsubscribe_event = cam_subdev_unsubscribe_event,
};
static const struct v4l2_subdev_ops cam_subdev_ops = {
.core = &cam_subdev_core_ops,
};
int cam_subdev_remove(struct cam_subdev *sd)
{
if (!sd)
return -EINVAL;
cam_unregister_subdev(sd);
cam_node_deinit((struct cam_node *)sd->token);
kfree(sd->token);
return 0;
}
EXPORT_SYMBOL_GPL(cam_subdev_remove);
int cam_subdev_probe(struct cam_subdev *sd, struct platform_device *pdev,
char *name, uint32_t dev_type)
{
int rc;
struct cam_node *node = NULL;
if (!sd || !pdev || !name)
return -EINVAL;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
/* Setup camera v4l2 subdevice */
sd->pdev = pdev;
sd->name = name;
sd->ops = &cam_subdev_ops;
sd->token = node;
sd->sd_flags =
V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->ent_function = dev_type;
rc = cam_register_subdev(sd);
if (rc) {
CAM_ERR(CAM_CORE, "cam_register_subdev() failed for dev: %s",
sd->name);
goto err;
}
platform_set_drvdata(sd->pdev, sd);
return rc;
err:
kfree(node);
return rc;
}
EXPORT_SYMBOL_GPL(cam_subdev_probe);