From a73b62eb5943fb95f8b157f7535c174f4a22df6c Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 14 Oct 2020 14:07:13 +0800 Subject: [PATCH] media: add hall-dc-motor driver for camera iris Signed-off-by: Zefa Chen Change-Id: Ic4207089ca6eb61895c57e8ddecadb7b2e382e7b --- drivers/media/i2c/Kconfig | 6 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/hall-dc-motor.c | 252 ++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 drivers/media/i2c/hall-dc-motor.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index f6caf5da870c..ad66fc2667c9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1649,6 +1649,12 @@ config VIDEO_I2C To compile this driver as a module, choose M here: the module will be called video-i2c +config VIDEO_HALL_DC_MOTOR + tristate "Hall dc-motor driver for camera iris" + depends on PWM && VIDEO_V4L2 + help + This a driver used pwm to drive motor + config VIDEO_RK_IRCUT tristate "Rockchip IR-CUT control device" depends on VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a5cec8054860..152c11c8c6cd 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_VIDEO_CS3308) += cs3308.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_M52790) += m52790.o +obj-$(CONFIG_VIDEO_HALL_DC_MOTOR) += hall-dc-motor.o obj-$(CONFIG_VIDEO_RK_IRCUT) += rk_ircut.o obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o diff --git a/drivers/media/i2c/hall-dc-motor.c b/drivers/media/i2c/hall-dc-motor.c new file mode 100644 index 000000000000..284a633d70db --- /dev/null +++ b/drivers/media/i2c/hall-dc-motor.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * motor driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + * + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk_vcm_head.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#define DRIVER_NAME "hall-dc" + +#define IRIS_MAX_LOG 100 +#define IRIS_LOG_STEP 1 + +#define PWM_PERIOD_DEF 333333 + +struct motor_dev { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrl_handler; + struct pwm_device *pwm; + struct device *dev; + struct mutex mutex; + struct pwm_state pwm_state; + u32 module_index; + const char *module_facing; +}; + +static int motor_dev_parse_dt(struct motor_dev *motor) +{ + struct device_node *node = motor->dev->of_node; + int ret = 0; + int error = 0; + + motor->pwm = devm_pwm_get(motor->dev, NULL); + if (IS_ERR(motor->pwm)) { + error = PTR_ERR(motor->pwm); + if (error != -EPROBE_DEFER) + dev_err(motor->dev, "Failed to request PWM device: %d\n", error); + return error; + } + if (motor->pwm && motor->pwm->args.period != 0) { + motor->pwm_state.period = motor->pwm->args.period; + motor->pwm_state.polarity = motor->pwm->args.polarity; + } else { + motor->pwm_state.period = PWM_PERIOD_DEF; + motor->pwm_state.polarity = 0; + } + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &motor->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &motor->module_facing); + if (ret) { + dev_err(motor->dev, + "could not get module information!\n"); + return -EINVAL; + } + return 0; +} + +static int motor_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret = 0; + struct motor_dev *motor = container_of(ctrl->handler, + struct motor_dev, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_IRIS_ABSOLUTE: + motor->pwm_state.enabled = true; + motor->pwm_state.duty_cycle = + (u64)motor->pwm_state.period * ctrl->val / IRIS_MAX_LOG; + pwm_apply_state(motor->pwm, &motor->pwm_state); + dev_dbg(motor->dev, "iris, ctrl->val %d, pwm duty %lld, period %lld, polarity %d\n", + ctrl->val, + motor->pwm_state.duty_cycle, + motor->pwm_state.period, + motor->pwm_state.polarity); + break; + default: + dev_err(motor->dev, "not support cmd %d\n", ctrl->id); + break; + } + return ret; +} + +static long motor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + return 0; +} + +static const struct v4l2_subdev_core_ops motor_core_ops = { + .ioctl = motor_ioctl, +}; + +static const struct v4l2_subdev_ops motor_subdev_ops = { + .core = &motor_core_ops, +}; + +static const struct v4l2_ctrl_ops motor_ctrl_ops = { + .s_ctrl = motor_s_ctrl, +}; + +static int motor_initialize_controls(struct motor_dev *motor) +{ + struct v4l2_ctrl_handler *handler; + int ret = 0; + + handler = &motor->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 1); + if (ret) + return ret; + handler->lock = &motor->mutex; + v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_IRIS_ABSOLUTE, 0, IRIS_MAX_LOG, IRIS_LOG_STEP, 0); + + if (handler->error) { + ret = handler->error; + dev_err(motor->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + motor->sd.ctrl_handler = handler; + return ret; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int motor_dev_probe(struct platform_device *pdev) +{ + int ret = 0; + struct motor_dev *motor; + struct v4l2_subdev *sd; + char facing[2]; + + dev_info(&pdev->dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + motor = devm_kzalloc(&pdev->dev, sizeof(*motor), GFP_KERNEL); + if (!motor) + return -ENOMEM; + motor->dev = &pdev->dev; + dev_set_name(motor->dev, "motor"); + dev_set_drvdata(motor->dev, motor); + if (motor_dev_parse_dt(motor)) { + dev_err(motor->dev, "parse dt error\n"); + return -EINVAL; + } + mutex_init(&motor->mutex); + v4l2_subdev_init(&motor->sd, &motor_subdev_ops); + motor->sd.owner = pdev->dev.driver->owner; + motor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + motor->sd.dev = &pdev->dev; + v4l2_set_subdevdata(&motor->sd, pdev); + platform_set_drvdata(pdev, &motor->sd); + motor_initialize_controls(motor); + if (ret) + goto err_free; + ret = media_entity_pads_init(&motor->sd.entity, 0, NULL); + if (ret < 0) + goto err_free; + sd = &motor->sd; + sd->entity.function = MEDIA_ENT_F_LENS; + sd->entity.flags = 2; + + memset(facing, 0, sizeof(facing)); + if (strcmp(motor->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s", + motor->module_index, facing, + DRIVER_NAME); + ret = v4l2_async_register_subdev(sd); + if (ret) + dev_err(&pdev->dev, "v4l2 async register subdev failed\n"); + + dev_info(motor->dev, "gpio motor driver probe success\n"); + return 0; +err_free: + v4l2_ctrl_handler_free(&motor->ctrl_handler); + v4l2_device_unregister_subdev(&motor->sd); + media_entity_cleanup(&motor->sd.entity); + return ret; +} + +static int motor_dev_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct motor_dev *motor; + + if (sd) + motor = v4l2_get_subdevdata(sd); + else + return -ENODEV; + if (sd) + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&motor->ctrl_handler); + media_entity_cleanup(&motor->sd.entity); + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id motor_dev_of_match[] = { + { .compatible = "rockchip,hall-dc", }, + {}, +}; +#endif + +static struct platform_driver motor_dev_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(motor_dev_of_match), + }, + .probe = motor_dev_probe, + .remove = motor_dev_remove, +}; + +module_platform_driver(motor_dev_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:motor"); +MODULE_AUTHOR("ROCKCHIP");