581 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			581 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * pisp_dmy driver
 | 
						|
 *
 | 
						|
 * Copyright (C) 2020 Rockchip Electronics Co., Ltd.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/clk.h>
 | 
						|
#include <linux/device.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/gpio/consumer.h>
 | 
						|
#include <linux/i2c.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/pm_runtime.h>
 | 
						|
#include <linux/regulator/consumer.h>
 | 
						|
#include <linux/sysfs.h>
 | 
						|
#include <media/media-entity.h>
 | 
						|
#include <media/v4l2-async.h>
 | 
						|
#include <media/v4l2-ctrls.h>
 | 
						|
#include <media/v4l2-subdev.h>
 | 
						|
#include <media/v4l2-fwnode.h>
 | 
						|
#include <linux/pinctrl/consumer.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include <linux/of_device.h>
 | 
						|
#include <linux/of_graph.h>
 | 
						|
#include <linux/of_platform.h>
 | 
						|
#include <linux/of_gpio.h>
 | 
						|
#include <linux/mfd/syscon.h>
 | 
						|
#include <linux/version.h>
 | 
						|
#include <linux/rk-camera-module.h>
 | 
						|
#include <linux/rk-preisp.h>
 | 
						|
 | 
						|
#define DRIVER_VERSION				KERNEL_VERSION(0, 0x01, 0x0)
 | 
						|
 | 
						|
#ifndef V4L2_CID_DIGITAL_GAIN
 | 
						|
#define V4L2_CID_DIGITAL_GAIN			V4L2_CID_GAIN
 | 
						|
#endif
 | 
						|
 | 
						|
#define PISP_DMY_XVCLK_FREQ			24000000
 | 
						|
 | 
						|
#define OF_CAMERA_PINCTRL_STATE_DEFAULT		"rockchip,camera_default"
 | 
						|
#define OF_CAMERA_PINCTRL_STATE_SLEEP		"rockchip,camera_sleep"
 | 
						|
 | 
						|
#define OF_CAMERA_MODULE_REGULATORS		"rockchip,regulator-names"
 | 
						|
#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES	"rockchip,regulator-voltages"
 | 
						|
 | 
						|
#define PISP_DMY_NAME				"pisp_dmy"
 | 
						|
 | 
						|
struct pisp_dmy_gpio {
 | 
						|
	int pltfrm_gpio;
 | 
						|
	const char *label;
 | 
						|
	enum of_gpio_flags active_low;
 | 
						|
};
 | 
						|
 | 
						|
struct pisp_dmy_regulator {
 | 
						|
	struct regulator *regulator;
 | 
						|
	u32 min_uV;
 | 
						|
	u32 max_uV;
 | 
						|
};
 | 
						|
 | 
						|
struct pisp_dmy_regulators {
 | 
						|
	u32 cnt;
 | 
						|
	struct pisp_dmy_regulator *regulator;
 | 
						|
};
 | 
						|
 | 
						|
struct pisp_dmy {
 | 
						|
	struct i2c_client	*client;
 | 
						|
	struct clk		*xvclk;
 | 
						|
	struct gpio_desc	*rst_gpio;
 | 
						|
	struct gpio_desc	*rst2_gpio;
 | 
						|
	struct gpio_desc	*pd_gpio;
 | 
						|
	struct gpio_desc	*pd2_gpio;
 | 
						|
	struct gpio_desc	*pwd_gpio;
 | 
						|
	struct gpio_desc	*pwd2_gpio;
 | 
						|
 | 
						|
	struct pinctrl		*pinctrl;
 | 
						|
	struct pinctrl_state	*pins_default;
 | 
						|
	struct pinctrl_state	*pins_sleep;
 | 
						|
 | 
						|
	struct v4l2_subdev	subdev;
 | 
						|
	struct media_pad	pad;
 | 
						|
	struct mutex		mutex;
 | 
						|
	bool			power_on;
 | 
						|
	struct pisp_dmy_regulators regulators;
 | 
						|
 | 
						|
	u32			module_index;
 | 
						|
	const char		*module_facing;
 | 
						|
	const char		*module_name;
 | 
						|
	const char		*len_name;
 | 
						|
};
 | 
						|
 | 
						|
#define to_pisp_dmy(sd) container_of(sd, struct pisp_dmy, subdev)
 | 
						|
 | 
						|
static int __pisp_dmy_power_on(struct pisp_dmy *pisp_dmy)
 | 
						|
{
 | 
						|
	u32 i;
 | 
						|
	int ret;
 | 
						|
	struct pisp_dmy_regulator *regulator;
 | 
						|
	struct device *dev = &pisp_dmy->client->dev;
 | 
						|
 | 
						|
	if (!IS_ERR_OR_NULL(pisp_dmy->pins_default)) {
 | 
						|
		ret = pinctrl_select_state(pisp_dmy->pinctrl,
 | 
						|
					   pisp_dmy->pins_default);
 | 
						|
		if (ret < 0)
 | 
						|
			dev_err(dev, "could not set pins. ret=%d\n", ret);
 | 
						|
	}
 | 
						|
 | 
						|
	ret = clk_prepare_enable(pisp_dmy->xvclk);
 | 
						|
	if (ret < 0) {
 | 
						|
		dev_err(dev, "Failed to enable xvclk\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	if (pisp_dmy->regulators.regulator) {
 | 
						|
		for (i = 0; i < pisp_dmy->regulators.cnt; i++) {
 | 
						|
			regulator = pisp_dmy->regulators.regulator + i;
 | 
						|
			if (IS_ERR(regulator->regulator))
 | 
						|
				continue;
 | 
						|
			regulator_set_voltage(
 | 
						|
				regulator->regulator,
 | 
						|
				regulator->min_uV,
 | 
						|
				regulator->max_uV);
 | 
						|
			if (regulator_enable(regulator->regulator)) {
 | 
						|
				dev_err(dev,
 | 
						|
					"regulator_enable failed!\n");
 | 
						|
				goto disable_clk;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	usleep_range(3000, 5000);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pwd_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->pwd_gpio, 1);
 | 
						|
		usleep_range(3000, 5000);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pwd2_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->pwd2_gpio, 1);
 | 
						|
		usleep_range(3000, 5000);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pd_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->pd_gpio, 1);
 | 
						|
		usleep_range(1500, 2000);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pd2_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->pd2_gpio, 1);
 | 
						|
		usleep_range(1500, 2000);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->rst_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->rst_gpio, 0);
 | 
						|
		usleep_range(1500, 2000);
 | 
						|
		gpiod_direction_output(pisp_dmy->rst_gpio, 1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->rst2_gpio)) {
 | 
						|
		gpiod_direction_output(pisp_dmy->rst2_gpio, 0);
 | 
						|
		usleep_range(1500, 2000);
 | 
						|
		gpiod_direction_output(pisp_dmy->rst2_gpio, 1);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
disable_clk:
 | 
						|
	clk_disable_unprepare(pisp_dmy->xvclk);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void __pisp_dmy_power_off(struct pisp_dmy *pisp_dmy)
 | 
						|
{
 | 
						|
	u32 i;
 | 
						|
	int ret;
 | 
						|
	struct pisp_dmy_regulator *regulator;
 | 
						|
	struct device *dev = &pisp_dmy->client->dev;
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pd_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->pd_gpio, 0);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pd2_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->pd2_gpio, 0);
 | 
						|
 | 
						|
	clk_disable_unprepare(pisp_dmy->xvclk);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->rst_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->rst_gpio, 0);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->rst2_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->rst2_gpio, 0);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pwd_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->pwd_gpio, 0);
 | 
						|
 | 
						|
	if (!IS_ERR(pisp_dmy->pwd2_gpio))
 | 
						|
		gpiod_direction_output(pisp_dmy->pwd2_gpio, 0);
 | 
						|
 | 
						|
	if (!IS_ERR_OR_NULL(pisp_dmy->pins_sleep)) {
 | 
						|
		ret = pinctrl_select_state(pisp_dmy->pinctrl,
 | 
						|
					   pisp_dmy->pins_sleep);
 | 
						|
		if (ret < 0)
 | 
						|
			dev_err(dev, "could not set pins\n");
 | 
						|
	}
 | 
						|
 | 
						|
	if (pisp_dmy->regulators.regulator) {
 | 
						|
		for (i = 0; i < pisp_dmy->regulators.cnt; i++) {
 | 
						|
			regulator = pisp_dmy->regulators.regulator + i;
 | 
						|
			if (IS_ERR(regulator->regulator))
 | 
						|
				continue;
 | 
						|
			regulator_disable(regulator->regulator);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int pisp_dmy_power(struct v4l2_subdev *sd, int on)
 | 
						|
{
 | 
						|
	struct pisp_dmy *pisp_dmy = to_pisp_dmy(sd);
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	mutex_lock(&pisp_dmy->mutex);
 | 
						|
 | 
						|
	/* If the power state is not modified - no work to do. */
 | 
						|
	if (pisp_dmy->power_on == !!on)
 | 
						|
		goto exit;
 | 
						|
 | 
						|
	if (on) {
 | 
						|
		ret = __pisp_dmy_power_on(pisp_dmy);
 | 
						|
		if (ret < 0)
 | 
						|
			goto exit;
 | 
						|
 | 
						|
		pisp_dmy->power_on = true;
 | 
						|
	} else {
 | 
						|
		__pisp_dmy_power_off(pisp_dmy);
 | 
						|
		pisp_dmy->power_on = false;
 | 
						|
	}
 | 
						|
 | 
						|
exit:
 | 
						|
	mutex_unlock(&pisp_dmy->mutex);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void pisp_dmy_get_module_inf(struct pisp_dmy *pisp_dmy,
 | 
						|
				    struct rkmodule_inf *inf)
 | 
						|
{
 | 
						|
	memset(inf, 0, sizeof(*inf));
 | 
						|
	strlcpy(inf->base.sensor, PISP_DMY_NAME, sizeof(inf->base.sensor));
 | 
						|
	strlcpy(inf->base.module, pisp_dmy->module_name,
 | 
						|
		sizeof(inf->base.module));
 | 
						|
	strlcpy(inf->base.lens, pisp_dmy->len_name, sizeof(inf->base.lens));
 | 
						|
}
 | 
						|
 | 
						|
static long pisp_dmy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 | 
						|
{
 | 
						|
	struct pisp_dmy *pisp_dmy = to_pisp_dmy(sd);
 | 
						|
	long ret = 0;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case RKMODULE_GET_MODULE_INFO:
 | 
						|
		pisp_dmy_get_module_inf(pisp_dmy, (struct rkmodule_inf *)arg);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		ret = -ENOTTY;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_COMPAT
 | 
						|
static long pisp_dmy_compat_ioctl32(struct v4l2_subdev *sd,
 | 
						|
				    unsigned int cmd, unsigned long arg)
 | 
						|
{
 | 
						|
	void __user *up = compat_ptr(arg);
 | 
						|
	struct rkmodule_inf *inf;
 | 
						|
	struct rkmodule_awb_cfg *cfg;
 | 
						|
	long ret;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case RKMODULE_GET_MODULE_INFO:
 | 
						|
		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
 | 
						|
		if (!inf) {
 | 
						|
			ret = -ENOMEM;
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = pisp_dmy_ioctl(sd, cmd, inf);
 | 
						|
		if (!ret)
 | 
						|
			if (copy_to_user(up, inf, sizeof(*inf))) {
 | 
						|
				kfree(inf);
 | 
						|
				return -EFAULT;
 | 
						|
			}
 | 
						|
		kfree(inf);
 | 
						|
		break;
 | 
						|
	case RKMODULE_AWB_CFG:
 | 
						|
		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
 | 
						|
		if (!cfg) {
 | 
						|
			ret = -ENOMEM;
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		if (copy_from_user(cfg, up, sizeof(*cfg))) {
 | 
						|
			kfree(cfg);
 | 
						|
			return -EFAULT;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = pisp_dmy_ioctl(sd, cmd, cfg);
 | 
						|
		kfree(cfg);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		ret = -ENOIOCTLCMD;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static int pisp_dmy_runtime_resume(struct device *dev)
 | 
						|
{
 | 
						|
	struct i2c_client *client = to_i2c_client(dev);
 | 
						|
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 | 
						|
	struct pisp_dmy *pisp_dmy = to_pisp_dmy(sd);
 | 
						|
 | 
						|
	return __pisp_dmy_power_on(pisp_dmy);
 | 
						|
}
 | 
						|
 | 
						|
static int pisp_dmy_runtime_suspend(struct device *dev)
 | 
						|
{
 | 
						|
	struct i2c_client *client = to_i2c_client(dev);
 | 
						|
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 | 
						|
	struct pisp_dmy *pisp_dmy = to_pisp_dmy(sd);
 | 
						|
 | 
						|
	__pisp_dmy_power_off(pisp_dmy);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dev_pm_ops pisp_dmy_pm_ops = {
 | 
						|
	SET_RUNTIME_PM_OPS(pisp_dmy_runtime_suspend,
 | 
						|
			   pisp_dmy_runtime_resume, NULL)
 | 
						|
};
 | 
						|
 | 
						|
static const struct v4l2_subdev_core_ops pisp_dmy_core_ops = {
 | 
						|
	.s_power = pisp_dmy_power,
 | 
						|
	.ioctl = pisp_dmy_ioctl,
 | 
						|
#ifdef CONFIG_COMPAT
 | 
						|
	.compat_ioctl32 = pisp_dmy_compat_ioctl32,
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
static const struct v4l2_subdev_ops pisp_dmy_subdev_ops = {
 | 
						|
	.core = &pisp_dmy_core_ops,
 | 
						|
};
 | 
						|
 | 
						|
static int pisp_dmy_analyze_dts(struct pisp_dmy *pisp_dmy)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	int elem_size, elem_index;
 | 
						|
	const char *str = "";
 | 
						|
	struct property *prop;
 | 
						|
	struct pisp_dmy_regulator *regulator;
 | 
						|
	struct device *dev = &pisp_dmy->client->dev;
 | 
						|
	struct device_node *np = of_node_get(dev->of_node);
 | 
						|
 | 
						|
	pisp_dmy->xvclk = devm_clk_get(dev, "xvclk");
 | 
						|
	if (IS_ERR(pisp_dmy->xvclk)) {
 | 
						|
		dev_err(dev, "Failed to get xvclk\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	ret = clk_set_rate(pisp_dmy->xvclk, PISP_DMY_XVCLK_FREQ);
 | 
						|
	if (ret < 0) {
 | 
						|
		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	if (clk_get_rate(pisp_dmy->xvclk) != PISP_DMY_XVCLK_FREQ)
 | 
						|
		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
 | 
						|
 | 
						|
	pisp_dmy->pinctrl = devm_pinctrl_get(dev);
 | 
						|
	if (!IS_ERR(pisp_dmy->pinctrl)) {
 | 
						|
		pisp_dmy->pins_default =
 | 
						|
			pinctrl_lookup_state(pisp_dmy->pinctrl,
 | 
						|
					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
 | 
						|
		if (IS_ERR(pisp_dmy->pins_default))
 | 
						|
			dev_err(dev, "could not get default pinstate\n");
 | 
						|
 | 
						|
		pisp_dmy->pins_sleep =
 | 
						|
			pinctrl_lookup_state(pisp_dmy->pinctrl,
 | 
						|
					     OF_CAMERA_PINCTRL_STATE_SLEEP);
 | 
						|
		if (IS_ERR(pisp_dmy->pins_sleep))
 | 
						|
			dev_err(dev, "could not get sleep pinstate\n");
 | 
						|
	} else {
 | 
						|
		dev_err(dev, "no pinctrl\n");
 | 
						|
	}
 | 
						|
 | 
						|
	elem_size = of_property_count_elems_of_size(
 | 
						|
		np,
 | 
						|
		OF_CAMERA_MODULE_REGULATOR_VOLTAGES,
 | 
						|
		sizeof(u32));
 | 
						|
	prop = of_find_property(
 | 
						|
		np,
 | 
						|
		OF_CAMERA_MODULE_REGULATORS,
 | 
						|
		NULL);
 | 
						|
	if (elem_size > 0 && !IS_ERR_OR_NULL(prop)) {
 | 
						|
		pisp_dmy->regulators.regulator =
 | 
						|
			devm_kzalloc(&pisp_dmy->client->dev,
 | 
						|
				     elem_size * sizeof(struct pisp_dmy_regulator),
 | 
						|
				     GFP_KERNEL);
 | 
						|
		if (!pisp_dmy->regulators.regulator) {
 | 
						|
			dev_err(dev, "could not malloc pisp_dmy_regulator\n");
 | 
						|
			return -ENOMEM;
 | 
						|
		}
 | 
						|
 | 
						|
		pisp_dmy->regulators.cnt = elem_size;
 | 
						|
 | 
						|
		str = NULL;
 | 
						|
		elem_index = 0;
 | 
						|
		regulator = pisp_dmy->regulators.regulator;
 | 
						|
		do {
 | 
						|
			str = of_prop_next_string(prop, str);
 | 
						|
			if (!str) {
 | 
						|
				dev_err(dev, "%s is not match %s in dts\n",
 | 
						|
					OF_CAMERA_MODULE_REGULATORS,
 | 
						|
					OF_CAMERA_MODULE_REGULATOR_VOLTAGES);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			regulator->regulator =
 | 
						|
				devm_regulator_get_optional(dev, str);
 | 
						|
			if (IS_ERR(regulator->regulator))
 | 
						|
				dev_err(dev, "devm_regulator_get %s failed\n",
 | 
						|
					str);
 | 
						|
			of_property_read_u32_index(
 | 
						|
				np,
 | 
						|
				OF_CAMERA_MODULE_REGULATOR_VOLTAGES,
 | 
						|
				elem_index++,
 | 
						|
				®ulator->min_uV);
 | 
						|
			regulator->max_uV = regulator->min_uV;
 | 
						|
			regulator++;
 | 
						|
		} while (--elem_size);
 | 
						|
	}
 | 
						|
 | 
						|
	pisp_dmy->pd_gpio = devm_gpiod_get(dev, "pd", GPIOD_OUT_LOW);
 | 
						|
	if (IS_ERR(pisp_dmy->pd_gpio))
 | 
						|
		dev_warn(dev, "can not find pd-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->pd_gpio));
 | 
						|
 | 
						|
	pisp_dmy->pd2_gpio = devm_gpiod_get(dev, "pd2", GPIOD_OUT_LOW);
 | 
						|
	if (IS_ERR(pisp_dmy->pd2_gpio))
 | 
						|
		dev_warn(dev, "can not find pd2-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->pd2_gpio));
 | 
						|
 | 
						|
	pisp_dmy->rst_gpio = devm_gpiod_get(dev, "rst", GPIOD_OUT_LOW);
 | 
						|
	if (IS_ERR(pisp_dmy->rst_gpio))
 | 
						|
		dev_warn(dev, "can not find rst-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->rst_gpio));
 | 
						|
 | 
						|
	pisp_dmy->rst2_gpio = devm_gpiod_get(dev, "rst2", GPIOD_OUT_LOW);
 | 
						|
	if (IS_ERR(pisp_dmy->rst2_gpio))
 | 
						|
		dev_warn(dev, "can not find rst2-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->rst2_gpio));
 | 
						|
 | 
						|
	pisp_dmy->pwd_gpio = devm_gpiod_get(dev, "pwd", GPIOD_OUT_HIGH);
 | 
						|
	if (IS_ERR(pisp_dmy->pwd_gpio))
 | 
						|
		dev_warn(dev, "can not find pwd-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->pwd_gpio));
 | 
						|
 | 
						|
	pisp_dmy->pwd2_gpio = devm_gpiod_get(dev, "pwd2", GPIOD_OUT_HIGH);
 | 
						|
	if (IS_ERR(pisp_dmy->pwd2_gpio))
 | 
						|
		dev_warn(dev, "can not find pwd2-gpios, error %ld\n",
 | 
						|
			 PTR_ERR(pisp_dmy->pwd2_gpio));
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int pisp_dmy_probe(struct i2c_client *client,
 | 
						|
			const struct i2c_device_id *id)
 | 
						|
{
 | 
						|
	struct device *dev = &client->dev;
 | 
						|
	struct device_node *node = dev->of_node;
 | 
						|
	struct pisp_dmy *pisp_dmy;
 | 
						|
	struct v4l2_subdev *sd;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	dev_info(dev, "driver version: %02x.%02x.%02x",
 | 
						|
		 DRIVER_VERSION >> 16,
 | 
						|
		 (DRIVER_VERSION & 0xff00) >> 8,
 | 
						|
		 DRIVER_VERSION & 0x00ff);
 | 
						|
 | 
						|
	pisp_dmy = devm_kzalloc(dev, sizeof(*pisp_dmy), GFP_KERNEL);
 | 
						|
	if (!pisp_dmy)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
 | 
						|
				   &pisp_dmy->module_index);
 | 
						|
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
 | 
						|
				       &pisp_dmy->module_facing);
 | 
						|
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
 | 
						|
				       &pisp_dmy->module_name);
 | 
						|
	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
 | 
						|
				       &pisp_dmy->len_name);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "could not get module information!\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	pisp_dmy->client = client;
 | 
						|
 | 
						|
	ret = pisp_dmy_analyze_dts(pisp_dmy);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "Failed to analyze dts\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_init(&pisp_dmy->mutex);
 | 
						|
 | 
						|
	sd = &pisp_dmy->subdev;
 | 
						|
	v4l2_i2c_subdev_init(sd, client, &pisp_dmy_subdev_ops);
 | 
						|
 | 
						|
	__pisp_dmy_power_on(pisp_dmy);
 | 
						|
 | 
						|
	pm_runtime_set_active(dev);
 | 
						|
	pm_runtime_enable(dev);
 | 
						|
	pm_runtime_idle(dev);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void pisp_dmy_remove(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 | 
						|
	struct pisp_dmy *pisp_dmy = to_pisp_dmy(sd);
 | 
						|
 | 
						|
	mutex_destroy(&pisp_dmy->mutex);
 | 
						|
 | 
						|
	pm_runtime_disable(&client->dev);
 | 
						|
	if (!pm_runtime_status_suspended(&client->dev))
 | 
						|
		__pisp_dmy_power_off(pisp_dmy);
 | 
						|
	pm_runtime_set_suspended(&client->dev);
 | 
						|
}
 | 
						|
 | 
						|
#if IS_ENABLED(CONFIG_OF)
 | 
						|
static const struct of_device_id pisp_dmy_of_match[] = {
 | 
						|
	{ .compatible = "pisp_dmy" },
 | 
						|
	{},
 | 
						|
};
 | 
						|
MODULE_DEVICE_TABLE(of, pisp_dmy_of_match);
 | 
						|
#endif
 | 
						|
 | 
						|
static const struct i2c_device_id pisp_dmy_match_id[] = {
 | 
						|
	{ "pisp_dmy", 0 },
 | 
						|
	{ },
 | 
						|
};
 | 
						|
 | 
						|
static struct i2c_driver pisp_dmy_i2c_driver = {
 | 
						|
	.driver = {
 | 
						|
		.name = PISP_DMY_NAME,
 | 
						|
		.pm = &pisp_dmy_pm_ops,
 | 
						|
		.of_match_table = of_match_ptr(pisp_dmy_of_match),
 | 
						|
	},
 | 
						|
	.probe		= &pisp_dmy_probe,
 | 
						|
	.remove		= &pisp_dmy_remove,
 | 
						|
	.id_table	= pisp_dmy_match_id,
 | 
						|
};
 | 
						|
 | 
						|
static int __init sensor_mod_init(void)
 | 
						|
{
 | 
						|
	return i2c_add_driver(&pisp_dmy_i2c_driver);
 | 
						|
}
 | 
						|
 | 
						|
static void __exit sensor_mod_exit(void)
 | 
						|
{
 | 
						|
	i2c_del_driver(&pisp_dmy_i2c_driver);
 | 
						|
}
 | 
						|
 | 
						|
device_initcall_sync(sensor_mod_init);
 | 
						|
module_exit(sensor_mod_exit);
 | 
						|
 | 
						|
MODULE_DESCRIPTION("preisp dummy sensor driver");
 | 
						|
MODULE_LICENSE("GPL v2");
 |