Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76f981f1 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: camera: Add IR-LED and IR-cut filter driver"

parents 65ae27bd 48b8c006
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8,3 +8,4 @@ obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_eeprom/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois/
obj-$(CONFIG_SPECTRA_CAMERA) += cam_ir_led/
+10 −0
Original line number Diff line number Diff line
ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/

obj-$(CONFIG_SPECTRA_CAMERA) += cam_ir_led_dev.o cam_ir_led_soc.o cam_ir_led_core.o
+233 −0
Original line number Diff line number Diff line
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "cam_ir_led_core.h"

static int cam_ir_cut_on(struct cam_ir_led_ctrl *ictrl)
{
	if (!ictrl) {
		CAM_ERR(CAM_IR_LED, "Ir_led control Null");
		return -EINVAL;
	}

	gpio_direction_output(
		ictrl->soc_info.gpio_data->cam_gpio_common_tbl[4].gpio, 0);
	gpio_direction_input(
		ictrl->soc_info.gpio_data->cam_gpio_common_tbl[3].gpio);

	return 0;
}

static int cam_ir_cut_off(struct cam_ir_led_ctrl *ictrl)
{
	if (!ictrl) {
		CAM_ERR(CAM_IR_LED, "Ir_led control Null");
		return -EINVAL;
	}

	gpio_direction_output(
		ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio, 0);
	gpio_direction_output(
		ictrl->soc_info.gpio_data->cam_gpio_common_tbl[3].gpio, 0);
	gpio_direction_input(
		ictrl->soc_info.gpio_data->cam_gpio_common_tbl[4].gpio);

	return 0;
}

static int cam_ir_led_set_intensity(struct cam_ir_led_ctrl *ictrl,
			uint32_t ir_led_intensity)
{
	CAM_INFO(CAM_IR_LED, "ir_led_intensity=%d", ir_led_intensity);
	switch (ir_led_intensity) {
	case IRLED_INTENSITY_OFF:
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio,
			0);
		break;

	case IRLED_INTENSITY_LEVEL1:
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio,
			1);
		break;

	case IRLED_INTENSITY_LEVEL2:
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio,
			0);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio,
			1);
		break;
	case IRLED_INTENSITY_LEVEL3:
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio,
			0);
		break;
	case IRLED_INTENSITY_LEVEL4:
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[0].gpio,
			1);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[1].gpio,
			0);
		gpio_direction_output(
			ictrl->soc_info.gpio_data->cam_gpio_common_tbl[2].gpio,
			0);
		break;
	}
	return 0;
}

int cam_ir_led_parser(struct cam_ir_led_ctrl *ictrl, void *arg)
{
	int rc = 0;
	uint32_t  *cmd_buf =  NULL;
	uintptr_t generic_ptr;
	uint32_t  *offset = NULL;
	size_t len_of_buffer;
	struct cam_control *ioctl_ctrl = NULL;
	struct cam_packet *csl_packet = NULL;
	struct cam_config_dev_cmd config;
	struct cam_cmd_buf_desc *cmd_desc = NULL;
	struct cam_ir_led_set_on_off *cam_ir_led_info = NULL;

	if (!ictrl || !arg) {
		CAM_ERR(CAM_IR_LED, "ictrl/arg is NULL");
		return -EINVAL;
	}
	/* getting CSL Packet */
	ioctl_ctrl = (struct cam_control *)arg;

	if (copy_from_user((&config), u64_to_user_ptr(ioctl_ctrl->handle),
		sizeof(config))) {
		CAM_ERR(CAM_IR_LED, "Copy cmd handle from user failed");
		rc = -EFAULT;
		return rc;
	}

	rc = cam_mem_get_cpu_buf(config.packet_handle,
		(uintptr_t *)&generic_ptr, &len_of_buffer);
	if (rc) {
		CAM_ERR(CAM_IR_LED, "Failed in getting the buffer : %d", rc);
		return rc;
	}

	if (config.offset > len_of_buffer) {
		CAM_ERR(CAM_IR_LED,
			"offset is out of bounds: offset: %lld len: %zu",
			config.offset, len_of_buffer);
		return -EINVAL;
	}

	/* Add offset to the ir_led csl header */
	csl_packet = (struct cam_packet *)(uintptr_t)(generic_ptr +
			config.offset);

	offset = (uint32_t *)((uint8_t *)&csl_packet->payload +
		csl_packet->cmd_buf_offset);
	cmd_desc = (struct cam_cmd_buf_desc *)(offset);
	rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle,
		(uintptr_t *)&generic_ptr, &len_of_buffer);
	if (rc < 0) {
		CAM_ERR(CAM_IR_LED, "Failed to get the command Buffer");
		return -EINVAL;
	}

	cmd_buf = (uint32_t *)((uint8_t *)generic_ptr +
		cmd_desc->offset);
	cam_ir_led_info = (struct cam_ir_led_set_on_off *)cmd_buf;

	switch (csl_packet->header.op_code & 0xFFFFFF) {
	case CAM_IR_LED_PACKET_OPCODE_ON:
		CAM_INFO(CAM_IR_LED, ":CAM_IR_LED_PACKET_OPCODE_ON");
		cam_ir_cut_on(ictrl);
		cam_ir_led_set_intensity(ictrl,
				cam_ir_led_info->ir_led_intensity);
		break;
	case CAM_IR_LED_PACKET_OPCODE_OFF:
		CAM_INFO(CAM_IR_LED, "CAM_IR_LED_PACKET_OPCODE_OFF");
		cam_ir_cut_off(ictrl);
		break;
	case CAM_PKT_NOP_OPCODE:
		CAM_INFO(CAM_IR_LED, "CAM_IR_LED: CAM_PKT_NOP_OPCODE");
		break;
	default:
		CAM_ERR(CAM_IR_LED, "Wrong Opcode : %d",
			(csl_packet->header.op_code & 0xFFFFFF));
		return -EINVAL;
	}

	return 0;
}

int cam_ir_led_stop_dev(struct cam_ir_led_ctrl *ictrl)
{
	int rc = 0;

	rc = cam_ir_cut_off(ictrl);

	return rc;
}

int cam_ir_led_release_dev(struct cam_ir_led_ctrl *ictrl)
{
	int rc = 0;

	if (ictrl->device_hdl != -1) {
		rc = cam_destroy_device_hdl(ictrl->device_hdl);
		if (rc)
			CAM_ERR(CAM_IR_LED,
				"Failed in destroying device handle rc = %d",
				rc);
		ictrl->device_hdl = -1;
	}

	return rc;
}

void cam_ir_led_shutdown(struct cam_ir_led_ctrl *ictrl)
{
	int rc;

	if (ictrl->ir_led_state == CAM_IR_LED_STATE_INIT)
		return;

	if ((ictrl->ir_led_state == CAM_IR_LED_STATE_CONFIG) ||
		(ictrl->ir_led_state == CAM_IR_LED_STATE_START)) {
		rc = cam_ir_led_stop_dev(ictrl);
		if (rc)
			CAM_ERR(CAM_IR_LED, "Stop Failed rc: %d", rc);
	}

	rc = cam_ir_led_release_dev(ictrl);
	if (rc)
		CAM_ERR(CAM_IR_LED, "Release failed rc: %d", rc);

	ictrl->ir_led_state = CAM_IR_LED_STATE_INIT;
}
+29 −0
Original line number Diff line number Diff line
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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 _CAM_IR_LED_CORE_H_
#define _CAM_IR_LED_CORE_H_
#include "cam_ir_led_dev.h"

#define IRLED_INTENSITY_OFF    0
#define IRLED_INTENSITY_LEVEL1 1
#define IRLED_INTENSITY_LEVEL2 2
#define IRLED_INTENSITY_LEVEL3 3
#define IRLED_INTENSITY_LEVEL4 4
#define IRLED_INTENSITY_MAX    5


int cam_ir_led_parser(struct cam_ir_led_ctrl *fctrl, void *arg);
void cam_ir_led_shutdown(struct cam_ir_led_ctrl *ir_led_ctrl);
int cam_ir_led_stop_dev(struct cam_ir_led_ctrl *ir_led_ctrl);
int cam_ir_led_release_dev(struct cam_ir_led_ctrl *fctrl);
#endif /*_CAM_IR_LED_CORE_H_*/
+384 −0
Original line number Diff line number Diff line
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/module.h>
#include "cam_ir_led_dev.h"
#include "cam_ir_led_soc.h"
#include "cam_ir_led_core.h"

static int32_t cam_ir_led_driver_cmd(struct cam_ir_led_ctrl *ictrl,
		void *arg, struct cam_ir_led_private_soc *soc_private)
{
	int rc = 0;
	struct cam_control *cmd = (struct cam_control *)arg;

		CAM_ERR(CAM_IR_LED, "%s, IN", __func__);
	if (!ictrl || !arg) {
		CAM_ERR(CAM_IR_LED, "ictrl/arg is NULL with arg:%pK ictrl%pK",
			ictrl, arg);
		return -EINVAL;
	}

	if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
		CAM_ERR(CAM_IR_LED, "Invalid handle type: %d",
			cmd->handle_type);
		return -EINVAL;
	}

	mutex_lock(&(ictrl->ir_led_mutex));
	switch (cmd->op_code) {
	case CAM_ACQUIRE_DEV: {
		struct cam_sensor_acquire_dev ir_led_acq_dev;
		struct cam_create_dev_hdl dev_hdl;

		CAM_DBG(CAM_IR_LED, "CAM_ACQUIRE_DEV");

		if (ictrl->ir_led_state != CAM_IR_LED_STATE_INIT) {
			CAM_ERR(CAM_IR_LED,
				" Cannot apply Acquire dev: Prev state: %d",
				ictrl->ir_led_state);
			rc = -EINVAL;
			goto release_mutex;
		}

		rc = copy_from_user(&ir_led_acq_dev,
			u64_to_user_ptr(cmd->handle),
			sizeof(ir_led_acq_dev));
		if (rc) {
			CAM_ERR(CAM_IR_LED, "Failed Copy from User rc=%d", rc);
			goto release_mutex;
		}

		dev_hdl.priv = ictrl;

		ir_led_acq_dev.device_handle =
			cam_create_device_hdl(&dev_hdl);
		ictrl->device_hdl =
			ir_led_acq_dev.device_handle;

		rc = copy_to_user(u64_to_user_ptr(cmd->handle), &ir_led_acq_dev,
			sizeof(struct cam_sensor_acquire_dev));
		if (rc) {
			CAM_ERR(CAM_IR_LED, "Failed Copy to User rc=%d", rc);
			rc = -EFAULT;
			goto release_mutex;
		}
		ictrl->ir_led_state = CAM_IR_LED_STATE_ACQUIRE;
		break;
	}
	case CAM_RELEASE_DEV: {
		CAM_DBG(CAM_IR_LED, "CAM_RELEASE_DEV");
		if ((ictrl->ir_led_state == CAM_IR_LED_STATE_INIT) ||
			(ictrl->ir_led_state == CAM_IR_LED_STATE_START)) {
			CAM_WARN(CAM_IR_LED,
				" Cannot apply Release dev: Prev state:%d",
				ictrl->ir_led_state);
		}

		if (ictrl->device_hdl == -1 &&
			ictrl->ir_led_state == CAM_IR_LED_STATE_ACQUIRE) {
			CAM_ERR(CAM_IR_LED,
				" Invalid Handle: device hdl: %d",
				ictrl->device_hdl);
			rc = -EINVAL;
			goto release_mutex;
		}
		rc = cam_ir_led_release_dev(ictrl);
		if (rc)
			CAM_ERR(CAM_IR_LED,
				" Failed in destroying the device Handle rc= %d",
				rc);
		ictrl->ir_led_state = CAM_IR_LED_STATE_INIT;
		break;
	}
	case CAM_QUERY_CAP: {
		struct cam_ir_led_query_cap_info ir_led_cap = {0};

		CAM_DBG(CAM_IR_LED, "CAM_QUERY_CAP");
		ir_led_cap.slot_info = ictrl->soc_info.index;

		if (copy_to_user(u64_to_user_ptr(cmd->handle), &ir_led_cap,
			sizeof(struct cam_ir_led_query_cap_info))) {
			CAM_ERR(CAM_IR_LED, " Failed Copy to User");
			rc = -EFAULT;
			goto release_mutex;
		}
		break;
	}
	case CAM_START_DEV: {
		CAM_DBG(CAM_IR_LED, "CAM_START_DEV");
		if ((ictrl->ir_led_state == CAM_IR_LED_STATE_INIT) ||
			(ictrl->ir_led_state == CAM_IR_LED_STATE_START)) {
			CAM_ERR(CAM_IR_LED,
				"Cannot apply Start Dev: Prev state: %d",
				ictrl->ir_led_state);
			rc = -EINVAL;
			goto release_mutex;
		}
		ictrl->ir_led_state = CAM_IR_LED_STATE_START;

		break;
	}
	case CAM_STOP_DEV: {
		CAM_DBG(CAM_IR_LED, "CAM_STOP_DEV ENTER");
		if (ictrl->ir_led_state != CAM_IR_LED_STATE_START) {
			CAM_WARN(CAM_IR_LED,
				" Cannot apply Stop dev: Prev state is: %d",
				ictrl->ir_led_state);
			rc = -EINVAL;
			goto release_mutex;
		}
		rc = cam_ir_led_stop_dev(ictrl);
		if (rc) {
			CAM_ERR(CAM_IR_LED, "Failed STOP_DEV: rc=%d\n", rc);
			goto release_mutex;
		}
		ictrl->ir_led_state = CAM_IR_LED_STATE_ACQUIRE;
		break;
	}
	case CAM_CONFIG_DEV: {
		CAM_DBG(CAM_IR_LED, "CAM_CONFIG_DEV");
		rc = cam_ir_led_parser(ictrl, arg);
		if (rc) {
			CAM_ERR(CAM_IR_LED, "Failed CONFIG_DEV: rc=%d\n", rc);
			goto release_mutex;
		}
		break;
	}
	default:
		CAM_ERR(CAM_IR_LED, "Invalid Opcode: %d", cmd->op_code);
		rc = -EINVAL;
	}

release_mutex:
	mutex_unlock(&(ictrl->ir_led_mutex));
	return rc;
}

static const struct of_device_id cam_ir_led_dt_match[] = {
	{.compatible = "qcom,camera-ir-led", .data = NULL},
	{}
};

static long cam_ir_led_subdev_ioctl(struct v4l2_subdev *sd,
	unsigned int cmd, void *arg)
{
	int rc = 0;
	struct cam_ir_led_ctrl *ictrl = NULL;
	struct cam_ir_led_private_soc *soc_private = NULL;

	CAM_DBG(CAM_IR_LED, "Enter");

	ictrl = v4l2_get_subdevdata(sd);
	soc_private = ictrl->soc_info.soc_private;

	switch (cmd) {
	case VIDIOC_CAM_CONTROL: {
		rc = cam_ir_led_driver_cmd(ictrl, arg,
			soc_private);
		break;
	}
	default:
		CAM_ERR(CAM_IR_LED, " Invalid ioctl cmd type");
		rc = -EINVAL;
		break;
	}

	CAM_DBG(CAM_IR_LED, "Exit");
	return rc;
}

#ifdef CONFIG_COMPAT
static long cam_ir_led_subdev_do_ioctl(struct v4l2_subdev *sd,
	unsigned int cmd, unsigned long arg)
{
	struct cam_control cmd_data;
	int32_t rc = 0;

	if (copy_from_user(&cmd_data, (void __user *)arg,
		sizeof(cmd_data))) {
		CAM_ERR(CAM_IR_LED,
			" Failed to copy from user_ptr=%pK size=%zu",
			(void __user *)arg, sizeof(cmd_data));
		return -EFAULT;
	}

	switch (cmd) {
	case VIDIOC_CAM_CONTROL: {
		rc = cam_ir_led_subdev_ioctl(sd, cmd, &cmd_data);
		if (rc)
			CAM_ERR(CAM_IR_LED, "cam_ir_led_ioctl failed");
		break;
	}
	default:
		CAM_ERR(CAM_IR_LED, " Invalid compat ioctl cmd_type:%d",
			cmd);
		rc = -EINVAL;
	}

	if (!rc) {
		if (copy_to_user((void __user *)arg, &cmd_data,
			sizeof(cmd_data))) {
			CAM_ERR(CAM_IR_LED,
				" Failed to copy to user_ptr=%pK size=%zu",
				(void __user *)arg, sizeof(cmd_data));
			rc = -EFAULT;
		}
	}

	return rc;
}
#endif

static int cam_ir_led_platform_remove(struct platform_device *pdev)
{
	struct cam_ir_led_ctrl *ictrl;

	ictrl = platform_get_drvdata(pdev);
	if (!ictrl) {
		CAM_ERR(CAM_IR_LED, " Ir_led device is NULL");
		return 0;
	}

	devm_kfree(&pdev->dev, ictrl);

	return 0;
}

static int cam_ir_led_subdev_close(struct v4l2_subdev *sd,
	struct v4l2_subdev_fh *fh)
{
	struct cam_ir_led_ctrl *ir_led_ctrl =
		v4l2_get_subdevdata(sd);

	if (!ir_led_ctrl) {
		CAM_ERR(CAM_IR_LED, " Ir_led ctrl ptr is NULL");
		return -EINVAL;
	}

	mutex_lock(&ir_led_ctrl->ir_led_mutex);
	cam_ir_led_shutdown(ir_led_ctrl);
	mutex_unlock(&ir_led_ctrl->ir_led_mutex);

	return 0;
}

static struct v4l2_subdev_core_ops cam_ir_led_subdev_core_ops = {
	.ioctl = cam_ir_led_subdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = cam_ir_led_subdev_do_ioctl
#endif
};

static struct v4l2_subdev_ops cam_ir_led_subdev_ops = {
	.core = &cam_ir_led_subdev_core_ops,
};

static const struct v4l2_subdev_internal_ops cam_ir_led_internal_ops = {
	.close = cam_ir_led_subdev_close,
};

static int32_t cam_ir_led_platform_probe(struct platform_device *pdev)
{
	int32_t rc = 0;
	struct cam_ir_led_ctrl *ir_led_ctrl = NULL;

	CAM_ERR(CAM_IR_LED, "DBG:Enter");
	if (!pdev->dev.of_node) {
		CAM_ERR(CAM_IR_LED, "of_node NULL");
		return -EINVAL;
	}

	ir_led_ctrl = kzalloc(sizeof(struct cam_ir_led_ctrl), GFP_KERNEL);
	if (!ir_led_ctrl)
		return -ENOMEM;

	ir_led_ctrl->pdev = pdev;
	ir_led_ctrl->soc_info.pdev = pdev;
	ir_led_ctrl->soc_info.dev = &pdev->dev;
	ir_led_ctrl->soc_info.dev_name = pdev->name;

	rc = cam_ir_led_get_dt_data(ir_led_ctrl, &ir_led_ctrl->soc_info);
	if (rc) {
		CAM_ERR(CAM_IR_LED, "cam_ir_led_get_dt_data failed rc=%d", rc);
		if (ir_led_ctrl->soc_info.soc_private != NULL) {
			kfree(ir_led_ctrl->soc_info.soc_private);
			ir_led_ctrl->soc_info.soc_private = NULL;
		}
		kfree(ir_led_ctrl);
		ir_led_ctrl = NULL;
		return -EINVAL;
	}

	ir_led_ctrl->v4l2_dev_str.internal_ops =
		&cam_ir_led_internal_ops;
	ir_led_ctrl->v4l2_dev_str.ops = &cam_ir_led_subdev_ops;
	ir_led_ctrl->v4l2_dev_str.name = CAMX_IR_LED_DEV_NAME;
	ir_led_ctrl->v4l2_dev_str.sd_flags =
		V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
	ir_led_ctrl->v4l2_dev_str.ent_function = CAM_IRLED_DEVICE_TYPE;
	ir_led_ctrl->v4l2_dev_str.token = ir_led_ctrl;

	rc = cam_register_subdev(&(ir_led_ctrl->v4l2_dev_str));
	if (rc) {
		CAM_ERR(CAM_IR_LED, "Fail to create subdev with %d", rc);
		goto free_resource;
	}
	ir_led_ctrl->device_hdl = -1;

	platform_set_drvdata(pdev, ir_led_ctrl);
	v4l2_set_subdevdata(&ir_led_ctrl->v4l2_dev_str.sd, ir_led_ctrl);

	mutex_init(&(ir_led_ctrl->ir_led_mutex));

	ir_led_ctrl->ir_led_state = CAM_IR_LED_STATE_INIT;
	CAM_ERR(CAM_IR_LED, "DBG:Probe success");
	return rc;
free_resource:
	kfree(ir_led_ctrl);
	return rc;
}

MODULE_DEVICE_TABLE(of, cam_ir_led_dt_match);

static struct platform_driver cam_ir_led_platform_driver = {
	.probe = cam_ir_led_platform_probe,
	.remove = cam_ir_led_platform_remove,
	.driver = {
		.name = "CAM-IR-LED-DRIVER",
		.owner = THIS_MODULE,
		.of_match_table = cam_ir_led_dt_match,
		.suppress_bind_attrs = true,
	},
};

static int __init cam_ir_led_init_module(void)
{
	int32_t rc = 0;

	rc = platform_driver_register(&cam_ir_led_platform_driver);
	if (rc)
		CAM_ERR(CAM_IR_LED, "platform probe for ir_led failed");

	return rc;
}

static void __exit cam_ir_led_exit_module(void)
{
	platform_driver_unregister(&cam_ir_led_platform_driver);
}

module_init(cam_ir_led_init_module);
module_exit(cam_ir_led_exit_module);
MODULE_DESCRIPTION("CAM IR_LED");
MODULE_LICENSE("GPL v2");
Loading