312 lines
7.2 KiB
C
312 lines
7.2 KiB
C
/*
|
|
* for modules (sensor/actuator/flash) power supply helper.
|
|
*
|
|
* Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
|
|
*
|
|
* Authors: Zhao Wei <zhaowei@allwinnertech.com>
|
|
* Yang Feng <yangfeng@allwinnertech.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "../vin-video/vin_core.h"
|
|
#include "vin_os.h"
|
|
#include "vin_supply.h"
|
|
#include "../platform/platform_cfg.h"
|
|
#include "../vin-csi/sunxi_csi.h"
|
|
#include "../vin-cci/cci_helper.h"
|
|
#include "../vin.h"
|
|
|
|
/*
|
|
* called by subdev in power on/off sequency
|
|
*/
|
|
struct modules_config *sd_to_modules(struct v4l2_subdev *sd)
|
|
{
|
|
struct vin_md *vind = dev_get_drvdata(sd->v4l2_dev->dev);
|
|
struct modules_config *module = NULL;
|
|
int i, j;
|
|
|
|
for (i = 0; i < VIN_MAX_DEV; i++) {
|
|
module = &vind->modules[i];
|
|
|
|
for (j = 0; j < MAX_DETECT_NUM; j++) {
|
|
if (!strcmp(module->sensors.inst[j].cam_name, sd->name))
|
|
return module;
|
|
|
|
if ((sd == module->modules.sensor[j].sd) ||
|
|
(sd == module->modules.act[j].sd) ||
|
|
(sd == module->modules.flash.sd))
|
|
return module;
|
|
}
|
|
}
|
|
vin_err("%s cannot find the match modules\n", sd->name);
|
|
return NULL;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sd_to_modules);
|
|
|
|
/*
|
|
*enable/disable pmic channel
|
|
*/
|
|
int vin_set_pmu_channel(struct v4l2_subdev *sd, enum pmic_channel pmic_ch,
|
|
enum on_off on_off)
|
|
{
|
|
int ret = 0;
|
|
#ifndef FPGA_VER
|
|
struct modules_config *modules = sd_to_modules(sd);
|
|
static int def_vol[MAX_POW_NUM] = {3300000, 3300000, 1800000,
|
|
3300000, 3300000};
|
|
struct vin_power *power = NULL;
|
|
|
|
if (NULL == modules)
|
|
return -1;
|
|
|
|
power = &modules->sensors.power[0];
|
|
if (on_off == OFF) {
|
|
if (NULL == power[pmic_ch].pmic)
|
|
return 0;
|
|
ret = regulator_disable(power[pmic_ch].pmic);
|
|
if (!regulator_is_enabled(power[pmic_ch].pmic)) {
|
|
vin_log(VIN_LOG_POWER, "regulator_is already disabled\n");
|
|
regulator_put(power[pmic_ch].pmic);
|
|
power[pmic_ch].pmic = NULL;
|
|
}
|
|
} else {
|
|
if (power[pmic_ch].pmic
|
|
&& regulator_is_enabled(power[pmic_ch].pmic)) {
|
|
vin_log(VIN_LOG_POWER, "regulator_is already enabled\n");
|
|
} else {
|
|
if (strcmp(power[pmic_ch].power_str, "")) {
|
|
power[pmic_ch].pmic =
|
|
regulator_get(NULL,
|
|
power[pmic_ch].power_str);
|
|
if (IS_ERR_OR_NULL(power[pmic_ch].pmic)) {
|
|
vin_err("get regulator %s error!\n",
|
|
power[pmic_ch].power_str);
|
|
power[pmic_ch].pmic = NULL;
|
|
return -1;
|
|
}
|
|
} else {
|
|
power[pmic_ch].pmic = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
ret =
|
|
regulator_set_voltage(power[pmic_ch].pmic,
|
|
power[pmic_ch].power_vol,
|
|
def_vol[pmic_ch]);
|
|
vin_log(VIN_LOG_POWER, "set regulator %s = %d,return %x\n",
|
|
power[pmic_ch].power_str, power[pmic_ch].power_vol,
|
|
ret);
|
|
ret = regulator_enable(power[pmic_ch].pmic);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vin_set_pmu_channel);
|
|
|
|
/*
|
|
*enable/disable master clock
|
|
*/
|
|
int vin_set_mclk(struct v4l2_subdev *sd, enum on_off on_off)
|
|
{
|
|
struct vin_md *vind = dev_get_drvdata(sd->v4l2_dev->dev);
|
|
struct modules_config *modules = sd_to_modules(sd);
|
|
struct vin_mclk_info *mclk = NULL;
|
|
char pin_name[20] = "";
|
|
int mclk_id = 0;
|
|
|
|
if (NULL == modules)
|
|
return -1;
|
|
|
|
#if defined(CONFIG_CCI_MODULE) || defined(CONFIG_CCI)
|
|
mclk_id = modules->bus_sel;
|
|
#else
|
|
mclk_id = modules->sensors.csi_sel;
|
|
#endif
|
|
if (mclk_id < 0) {
|
|
vin_err("get mclk id failed\n");
|
|
return -1;
|
|
}
|
|
|
|
mclk = &vind->mclk[mclk_id];
|
|
|
|
switch (on_off) {
|
|
case ON:
|
|
csi_cci_init_helper(modules->bus_sel);
|
|
sprintf(pin_name, "mclk%d-default", mclk_id);
|
|
break;
|
|
case OFF:
|
|
csi_cci_exit_helper(modules->bus_sel);
|
|
sprintf(pin_name, "mclk%d-sleep", mclk_id);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
#ifndef FPGA_VER
|
|
|
|
if (on_off && mclk->use_count++ > 0)
|
|
return 0;
|
|
else if (!on_off && (mclk->use_count == 0 || --mclk->use_count > 0))
|
|
return 0;
|
|
|
|
switch (on_off) {
|
|
case ON:
|
|
vin_print("sensor mclk on, use_count %d!\n", mclk->use_count);
|
|
if (mclk->mclk) {
|
|
if (clk_prepare_enable(mclk->mclk)) {
|
|
vin_err("csi master clock enable error\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
vin_err("csi master%d clock is null\n", mclk_id);
|
|
return -1;
|
|
}
|
|
break;
|
|
case OFF:
|
|
vin_print("sensor mclk off, use_count %d!\n", mclk->use_count);
|
|
if (mclk->mclk) {
|
|
clk_disable_unprepare(mclk->mclk);
|
|
} else {
|
|
vin_err("csi master%d clock is null\n", mclk_id);
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
mclk->pin = devm_pinctrl_get_select(&vind->pdev->dev, pin_name);
|
|
if (IS_ERR_OR_NULL(mclk->pin)) {
|
|
vin_err("mclk%d request pin handle failed!\n", mclk_id);
|
|
return -EINVAL;
|
|
}
|
|
usleep_range(5000, 6000);
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vin_set_mclk);
|
|
|
|
/*
|
|
*set frequency of master clock
|
|
*/
|
|
int vin_set_mclk_freq(struct v4l2_subdev *sd, unsigned long freq)
|
|
{
|
|
#ifndef FPGA_VER
|
|
struct vin_md *vind = dev_get_drvdata(sd->v4l2_dev->dev);
|
|
struct modules_config *modules = sd_to_modules(sd);
|
|
struct clk *mclk_src = NULL;
|
|
int mclk_id = 0;
|
|
|
|
if (NULL == modules)
|
|
return -1;
|
|
|
|
#if defined(CONFIG_CCI_MODULE) || defined(CONFIG_CCI)
|
|
mclk_id = modules->bus_sel;
|
|
#else
|
|
mclk_id = modules->sensors.csi_sel;
|
|
#endif
|
|
if (mclk_id < 0) {
|
|
vin_err("get mclk id failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (freq == 24000000 || freq == 12000000 || freq == 6000000) {
|
|
if (vind->mclk[mclk_id].clk_24m) {
|
|
mclk_src = vind->mclk[mclk_id].clk_24m;
|
|
} else {
|
|
vin_err("csi master clock 24M source is null\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (vind->mclk[mclk_id].clk_pll) {
|
|
mclk_src = vind->mclk[mclk_id].clk_pll;
|
|
} else {
|
|
vin_err("csi master clock pll source is null\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (vind->mclk[mclk_id].mclk) {
|
|
if (clk_set_parent(vind->mclk[mclk_id].mclk, mclk_src)) {
|
|
vin_err("set mclk source failed!\n");
|
|
return -1;
|
|
}
|
|
if (clk_set_rate(vind->mclk[mclk_id].mclk, freq)) {
|
|
vin_err("set csi master%d clock error\n", mclk_id);
|
|
return -1;
|
|
}
|
|
} else {
|
|
vin_err("csi master clock is null\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vin_set_mclk_freq);
|
|
|
|
/*
|
|
*set the gpio io status
|
|
*/
|
|
int vin_gpio_write(struct v4l2_subdev *sd, enum gpio_type gpio_id,
|
|
unsigned int out_value)
|
|
{
|
|
#ifndef FPGA_VER
|
|
int force_value_flag = 1;
|
|
struct modules_config *modules = sd_to_modules(sd);
|
|
struct gpio_config *gc = NULL;
|
|
|
|
if (NULL == modules)
|
|
return -1;
|
|
|
|
gc = &modules->sensors.gpio[gpio_id];
|
|
if ((gpio_id == PWDN) || (gpio_id == RESET))
|
|
force_value_flag = 0;
|
|
return os_gpio_write(gc->gpio, out_value, force_value_flag);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(vin_gpio_write);
|
|
|
|
/*
|
|
*set the gpio io status
|
|
*/
|
|
int vin_gpio_set_status(struct v4l2_subdev *sd, enum gpio_type gpio_id,
|
|
unsigned int status)
|
|
{
|
|
#ifndef FPGA_VER
|
|
struct modules_config *modules = sd_to_modules(sd);
|
|
struct gpio_config gc_def;
|
|
struct gpio_config *gc = NULL;
|
|
|
|
if (NULL == modules)
|
|
return -1;
|
|
|
|
gc = &modules->sensors.gpio[gpio_id];
|
|
|
|
memcpy(&gc_def, gc, sizeof(struct gpio_config));
|
|
|
|
if (status == 0)
|
|
gc_def.mul_sel = GPIO_DISABLE;
|
|
|
|
os_gpio_set(&gc_def);
|
|
|
|
if (status == 1)
|
|
gpio_direction_output(gc->gpio, 0);
|
|
else
|
|
gpio_direction_input(gc->gpio);
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vin_gpio_set_status);
|
|
|
|
MODULE_AUTHOR("raymonxiu");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_DESCRIPTION("Video front end subdev for sunxi");
|