874 lines
21 KiB
C
874 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
/*
|
|
* MAIN AF voice coil motor driver
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/uaccess.h>
|
|
#ifdef CONFIG_COMPAT
|
|
#include <linux/compat.h>
|
|
#endif
|
|
|
|
/* kernel standard */
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
/* OIS/EIS Timer & Workqueue */
|
|
#include <linux/hrtimer.h>
|
|
#include <linux/init.h>
|
|
#include <linux/ktime.h>
|
|
/* ------------------------- */
|
|
|
|
#if defined(CONFIG_MACH_MT6779)
|
|
#include <archcounter_timesync.h>
|
|
#endif
|
|
|
|
#include "lens_info.h"
|
|
#include "lens_list.h"
|
|
|
|
#define AF_DRVNAME "MAINAF"
|
|
|
|
#if defined(CONFIG_MTK_LEGACY)
|
|
#define I2C_CONFIG_SETTING 1
|
|
#elif defined(CONFIG_OF)
|
|
#define I2C_CONFIG_SETTING 2 /* device tree */
|
|
#else
|
|
|
|
#define I2C_CONFIG_SETTING 1
|
|
#endif
|
|
|
|
#if I2C_CONFIG_SETTING == 1
|
|
#define LENS_I2C_BUSNUM 0
|
|
#define I2C_REGISTER_ID 0x28
|
|
#endif
|
|
|
|
#define PLATFORM_DRIVER_NAME "lens_actuator_main_af"
|
|
#define AF_DRIVER_CLASS_NAME "actuatordrv_main_af"
|
|
|
|
#if I2C_CONFIG_SETTING == 1
|
|
static struct i2c_board_info kd_lens_dev __initdata = {
|
|
I2C_BOARD_INFO(AF_DRVNAME, I2C_REGISTER_ID)};
|
|
#endif
|
|
|
|
#define AF_DEBUG
|
|
#ifdef AF_DEBUG
|
|
#define LOG_INF(format, args...) \
|
|
pr_info(AF_DRVNAME " [%s] " format, __func__, ##args)
|
|
#else
|
|
#define LOG_INF(format, args...)
|
|
#endif
|
|
|
|
/* OIS/EIS Timer & Workqueue */
|
|
static struct workqueue_struct *ois_workqueue;
|
|
static struct work_struct ois_work;
|
|
static struct hrtimer ois_timer;
|
|
|
|
static DEFINE_MUTEX(ois_mutex);
|
|
static int g_EnableTimer;
|
|
static int g_GetOisInfoCnt;
|
|
static int g_OisPosIdx;
|
|
static struct stAF_OisPosInfo OisPosInfo;
|
|
/* ------------------------- */
|
|
|
|
static struct stAF_DrvList g_stAF_DrvList[MAX_NUM_OF_LENS] = {
|
|
{1, AFDRV_DW9718TAF, DW9718TAF_SetI2Cclient, DW9718TAF_Ioctl,
|
|
DW9718TAF_Release, DW9718TAF_GetFileName, NULL},
|
|
{1, AFDRV_GT9772AF, GT9772AF_SetI2Cclient, GT9772AF_Ioctl,
|
|
GT9772AF_Release, GT9772AF_GetFileName, NULL},
|
|
{1, AFDRV_AK7371AF, AK7371AF_SetI2Cclient, AK7371AF_Ioctl,
|
|
AK7371AF_Release, AK7371AF_GetFileName, NULL},
|
|
{1, AFDRV_BU6424AF, BU6424AF_SetI2Cclient, BU6424AF_Ioctl,
|
|
BU6424AF_Release, BU6424AF_GetFileName, NULL},
|
|
{1, AFDRV_BU6429AF, BU6429AF_SetI2Cclient, BU6429AF_Ioctl,
|
|
BU6429AF_Release, BU6429AF_GetFileName, NULL},
|
|
{1, AFDRV_BU64748AF, bu64748af_SetI2Cclient_Main, bu64748af_Ioctl_Main,
|
|
bu64748af_Release_Main, bu64748af_GetFileName_Main, NULL},
|
|
{1, AFDRV_BU64253GWZAF, BU64253GWZAF_SetI2Cclient, BU64253GWZAF_Ioctl,
|
|
BU64253GWZAF_Release, BU64253GWZAF_GetFileName, NULL},
|
|
{1,
|
|
#ifdef CONFIG_MTK_LENS_BU63165AF_SUPPORT
|
|
AFDRV_BU63165AF, BU63165AF_SetI2Cclient, BU63165AF_Ioctl,
|
|
BU63165AF_Release, BU63165AF_GetFileName, NULL
|
|
#else
|
|
AFDRV_BU63169AF, BU63169AF_SetI2Cclient, BU63169AF_Ioctl,
|
|
BU63169AF_Release, BU63169AF_GetFileName, NULL
|
|
#endif
|
|
},
|
|
{1, AFDRV_DW9714AF, DW9714AF_SetI2Cclient, DW9714AF_Ioctl,
|
|
DW9714AF_Release, DW9714AF_GetFileName, NULL},
|
|
{1, AFDRV_DW9718SAF, DW9718SAF_SetI2Cclient, DW9718SAF_Ioctl,
|
|
DW9718SAF_Release, DW9718SAF_GetFileName, NULL},
|
|
{1, AFDRV_DW9719TAF, DW9719TAF_SetI2Cclient, DW9719TAF_Ioctl,
|
|
DW9719TAF_Release, DW9719TAF_GetFileName, NULL},
|
|
{1, AFDRV_DW9763AF, DW9763AF_SetI2Cclient, DW9763AF_Ioctl,
|
|
DW9763AF_Release, DW9763AF_GetFileName, NULL},
|
|
{1, AFDRV_LC898212XDAF, LC898212XDAF_SetI2Cclient, LC898212XDAF_Ioctl,
|
|
LC898212XDAF_Release, LC898212XDAF_GetFileName, NULL},
|
|
{1, AFDRV_DW9800WAF, DW9800WAF_SetI2Cclient, DW9800WAF_Ioctl,
|
|
DW9800WAF_Release, DW9800WAF_GetFileName, NULL},
|
|
{1, AFDRV_DW9814AF, DW9814AF_SetI2Cclient, DW9814AF_Ioctl,
|
|
DW9814AF_Release, DW9814AF_GetFileName, NULL},
|
|
{1, AFDRV_DW9839AF, DW9839AF_SetI2Cclient, DW9839AF_Ioctl,
|
|
DW9839AF_Release, DW9839AF_GetFileName, NULL},
|
|
{1, AFDRV_FP5510E2AF, FP5510E2AF_SetI2Cclient, FP5510E2AF_Ioctl,
|
|
FP5510E2AF_Release, FP5510E2AF_GetFileName, NULL},
|
|
{1, AFDRV_DW9718AF, DW9718AF_SetI2Cclient, DW9718AF_Ioctl,
|
|
DW9718AF_Release, DW9718AF_GetFileName, NULL},
|
|
{1, AFDRV_DW9718GAF, DW9718GAF_SetI2Cclient, DW9718GAF_Ioctl,
|
|
DW9718GAF_Release, DW9718GAF_GetFileName, NULL},
|
|
{1, AFDRV_GT9764AF, GT9764AF_SetI2Cclient, GT9764AF_Ioctl,
|
|
GT9764AF_Release, GT9764AF_GetFileName, NULL},
|
|
//#ifdef SUPPORT_GT9768AF
|
|
{1, AFDRV_GT9768AF, GT9768AF_SetI2Cclient, GT9768AF_Ioctl,
|
|
GT9768AF_Release, GT9768AF_GetFileName, NULL},
|
|
//#endif
|
|
{1, AFDRV_LC898212AF, LC898212AF_SetI2Cclient, LC898212AF_Ioctl,
|
|
LC898212AF_Release, LC898212AF_GetFileName, NULL},
|
|
{1, AFDRV_LC898214AF, LC898214AF_SetI2Cclient, LC898214AF_Ioctl,
|
|
LC898214AF_Release, LC898214AF_GetFileName, NULL},
|
|
{1, AFDRV_LC898217AF, LC898217AF_SetI2Cclient, LC898217AF_Ioctl,
|
|
LC898217AF_Release, LC898217AF_GetFileName, NULL},
|
|
{1, AFDRV_LC898217AFA, LC898217AFA_SetI2Cclient, LC898217AFA_Ioctl,
|
|
LC898217AFA_Release, LC898217AFA_GetFileName, NULL},
|
|
{1, AFDRV_LC898217AFB, LC898217AFB_SetI2Cclient, LC898217AFB_Ioctl,
|
|
LC898217AFB_Release, LC898217AFB_GetFileName, NULL},
|
|
{1, AFDRV_LC898217AFC, LC898217AFC_SetI2Cclient, LC898217AFC_Ioctl,
|
|
LC898217AFC_Release, LC898217AFC_GetFileName, NULL},
|
|
{1, AFDRV_LC898229AF, LC898229AF_SetI2Cclient, LC898229AF_Ioctl,
|
|
LC898229AF_Release, LC898229AF_GetFileName, NULL},
|
|
{1, AFDRV_LC898122AF, LC898122AF_SetI2Cclient, LC898122AF_Ioctl,
|
|
LC898122AF_Release, LC898122AF_GetFileName, NULL},
|
|
{1, AFDRV_WV511AAF, WV511AAF_SetI2Cclient, WV511AAF_Ioctl,
|
|
WV511AAF_Release, WV511AAF_GetFileName, NULL},
|
|
};
|
|
|
|
static struct stAF_DrvList *g_pstAF_CurDrv;
|
|
|
|
static spinlock_t g_AF_SpinLock;
|
|
|
|
static int g_s4AF_Opened;
|
|
|
|
static struct i2c_client *g_pstAF_I2Cclient;
|
|
|
|
static dev_t g_AF_devno;
|
|
static struct cdev *g_pAF_CharDrv;
|
|
static struct class *actuator_class;
|
|
static struct device *lens_device;
|
|
|
|
static struct regulator *vcamaf_ldo;
|
|
static struct pinctrl *vcamaf_pio;
|
|
static struct pinctrl_state *vcamaf_pio_on;
|
|
static struct pinctrl_state *vcamaf_pio_off;
|
|
|
|
#define CAMAF_PMIC "camaf_m1_pmic"
|
|
#define CAMAF_GPIO_ON "cam0_ldo_vcamaf_1"
|
|
#define CAMAF_GPIO_OFF "cam0_ldo_vcamaf_0"
|
|
|
|
static void camaf_power_init(void)
|
|
{
|
|
int ret;
|
|
struct device_node *node, *kd_node;
|
|
|
|
/* check if customer camera node defined */
|
|
node = of_find_compatible_node(
|
|
NULL, NULL, "mediatek,camera_af_lens");
|
|
|
|
if (node) {
|
|
kd_node = lens_device->of_node;
|
|
lens_device->of_node = node;
|
|
|
|
if (vcamaf_ldo == NULL) {
|
|
vcamaf_ldo = regulator_get(lens_device, CAMAF_PMIC);
|
|
if (IS_ERR(vcamaf_ldo)) {
|
|
ret = PTR_ERR(vcamaf_ldo);
|
|
vcamaf_ldo = NULL;
|
|
LOG_INF("cannot get regulator\n");
|
|
}
|
|
}
|
|
|
|
if (vcamaf_pio == NULL) {
|
|
vcamaf_pio = devm_pinctrl_get(lens_device);
|
|
if (IS_ERR(vcamaf_pio)) {
|
|
ret = PTR_ERR(vcamaf_pio);
|
|
vcamaf_pio = NULL;
|
|
pr_info("cannot get pinctrl\n");
|
|
} else {
|
|
vcamaf_pio_on = pinctrl_lookup_state(
|
|
vcamaf_pio, CAMAF_GPIO_ON);
|
|
|
|
if (IS_ERR(vcamaf_pio_on)) {
|
|
ret = PTR_ERR(vcamaf_pio_on);
|
|
vcamaf_pio_on = NULL;
|
|
LOG_INF("cannot get vcamaf_pio_on\n");
|
|
}
|
|
|
|
vcamaf_pio_off = pinctrl_lookup_state(
|
|
vcamaf_pio, CAMAF_GPIO_OFF);
|
|
|
|
if (IS_ERR(vcamaf_pio_off)) {
|
|
ret = PTR_ERR(vcamaf_pio_off);
|
|
vcamaf_pio_off = NULL;
|
|
LOG_INF("cannot get vcamaf_pio_off\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
lens_device->of_node = kd_node;
|
|
}
|
|
}
|
|
|
|
static void camaf_power_on(void)
|
|
{
|
|
int ret;
|
|
|
|
if (vcamaf_ldo) {
|
|
ret = regulator_enable(vcamaf_ldo);
|
|
LOG_INF("regulator enable (%d)\n", ret);
|
|
}
|
|
|
|
if (vcamaf_pio && vcamaf_pio_on) {
|
|
ret = pinctrl_select_state(vcamaf_pio, vcamaf_pio_on);
|
|
LOG_INF("pinctrl enable (%d)\n", ret);
|
|
}
|
|
}
|
|
|
|
static void camaf_power_off(void)
|
|
{
|
|
int ret;
|
|
|
|
if (vcamaf_ldo) {
|
|
ret = regulator_disable(vcamaf_ldo);
|
|
LOG_INF("regulator disable (%d)\n", ret);
|
|
}
|
|
|
|
if (vcamaf_pio && vcamaf_pio_off) {
|
|
ret = pinctrl_select_state(vcamaf_pio, vcamaf_pio_off);
|
|
LOG_INF("pinctrl disable (%d)\n", ret);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_MACH_MT6765
|
|
static int DrvPwrDn1 = 1;
|
|
static int DrvPwrDn2 = 1;
|
|
static int DrvPwrDn3 = 1;
|
|
#endif
|
|
|
|
void AF_PowerDown(void)
|
|
{
|
|
if (g_pstAF_I2Cclient != NULL) {
|
|
#if defined(CONFIG_MACH_MT6771) || \
|
|
defined(CONFIG_MACH_MT6775)
|
|
LC898217AF_PowerDown(g_pstAF_I2Cclient, &g_s4AF_Opened);
|
|
#endif
|
|
|
|
#ifdef CONFIG_MTK_LENS_AK7371AF_SUPPORT
|
|
AK7371AF_PowerDown(g_pstAF_I2Cclient, &g_s4AF_Opened);
|
|
#endif
|
|
|
|
#ifdef CONFIG_MACH_MT6758
|
|
AK7371AF_PowerDown(g_pstAF_I2Cclient, &g_s4AF_Opened);
|
|
|
|
BU63169AF_PowerDown(g_pstAF_I2Cclient, &g_s4AF_Opened);
|
|
#endif
|
|
|
|
#ifdef CONFIG_MACH_MT6765
|
|
int Ret1 = 0, Ret2 = 0, Ret3 = 0;
|
|
|
|
if (DrvPwrDn1) {
|
|
Ret1 = LC898217AF_PowerDown(g_pstAF_I2Cclient,
|
|
&g_s4AF_Opened);
|
|
}
|
|
|
|
if (DrvPwrDn2) {
|
|
Ret2 = DW9718SAF_PowerDown(g_pstAF_I2Cclient,
|
|
&g_s4AF_Opened);
|
|
}
|
|
|
|
if (DrvPwrDn3) {
|
|
Ret3 = bu64748af_PowerDown_Main(g_pstAF_I2Cclient,
|
|
&g_s4AF_Opened);
|
|
}
|
|
|
|
if (DrvPwrDn1 && DrvPwrDn2 && DrvPwrDn3) {
|
|
if (Ret1 < 0)
|
|
DrvPwrDn1 = 0;
|
|
if (Ret2 < 0)
|
|
DrvPwrDn2 = 0;
|
|
if (Ret3 < 0)
|
|
DrvPwrDn3 = 0;
|
|
|
|
}
|
|
LOG_INF("%d/%d , %d/%d, %d/%d\n", Ret1, DrvPwrDn1,
|
|
Ret2, DrvPwrDn2, Ret3, DrvPwrDn3);
|
|
#endif
|
|
|
|
#ifdef CONFIG_MACH_MT6761
|
|
DW9718SAF_PowerDown(g_pstAF_I2Cclient, &g_s4AF_Opened);
|
|
#endif
|
|
}
|
|
// MAIN2AF_PowerDown();
|
|
}
|
|
EXPORT_SYMBOL(AF_PowerDown);
|
|
|
|
static long AF_SetMotorName(__user struct stAF_MotorName *pstMotorName)
|
|
{
|
|
long i4RetValue = -1;
|
|
int i;
|
|
struct stAF_MotorName stMotorName;
|
|
|
|
if (copy_from_user(&stMotorName, pstMotorName,
|
|
sizeof(struct stAF_MotorName)))
|
|
LOG_INF("copy to user failed when getting motor information\n");
|
|
|
|
stMotorName.uMotorName[sizeof(stMotorName.uMotorName) - 1] = '\0';
|
|
|
|
for (i = 0; i < MAX_NUM_OF_LENS; i++) {
|
|
if (g_stAF_DrvList[i].uEnable != 1)
|
|
break;
|
|
|
|
LOG_INF("Search Motor Name : %s\n", g_stAF_DrvList[i].uDrvName);
|
|
if (strcmp(stMotorName.uMotorName,
|
|
g_stAF_DrvList[i].uDrvName) == 0) {
|
|
LOG_INF("Motor Name : %s\n", stMotorName.uMotorName);
|
|
g_pstAF_CurDrv = &g_stAF_DrvList[i];
|
|
i4RetValue = g_pstAF_CurDrv->pAF_SetI2Cclient(
|
|
g_pstAF_I2Cclient, &g_AF_SpinLock,
|
|
&g_s4AF_Opened);
|
|
break;
|
|
}
|
|
}
|
|
return i4RetValue;
|
|
}
|
|
|
|
|
|
static long AF_ControlParam(unsigned long a_u4Param)
|
|
{
|
|
long i4RetValue = -1;
|
|
__user struct stAF_CtrlCmd *pCtrlCmd =
|
|
(__user struct stAF_CtrlCmd *)a_u4Param;
|
|
struct stAF_CtrlCmd CtrlCmd;
|
|
|
|
if (copy_from_user(&CtrlCmd, pCtrlCmd, sizeof(struct stAF_CtrlCmd)))
|
|
LOG_INF("copy to user failed\n");
|
|
|
|
switch (CtrlCmd.i8CmdID) {
|
|
case CONVERT_CCU_TIMESTAMP:
|
|
{
|
|
#if defined(CONFIG_MACH_MT6779)
|
|
long long monotonicTime = 0;
|
|
long long hwTickCnt = 0;
|
|
|
|
hwTickCnt = CtrlCmd.i8Param[0];
|
|
monotonicTime = archcounter_timesync_to_monotonic(hwTickCnt);
|
|
/* do_div(monotonicTime, 1000); */ /* ns to us */
|
|
CtrlCmd.i8Param[0] = monotonicTime;
|
|
|
|
hwTickCnt = CtrlCmd.i8Param[1];
|
|
monotonicTime = archcounter_timesync_to_monotonic(hwTickCnt);
|
|
/* do_div(monotonicTime, 1000); */ /* ns to us */
|
|
CtrlCmd.i8Param[1] = monotonicTime;
|
|
|
|
if (copy_to_user(pCtrlCmd, &CtrlCmd,
|
|
sizeof(struct stAF_CtrlCmd)))
|
|
LOG_INF("copy to user failed\n");
|
|
#endif
|
|
}
|
|
i4RetValue = 1;
|
|
break;
|
|
default:
|
|
i4RetValue = -1;
|
|
break;
|
|
}
|
|
|
|
return i4RetValue;
|
|
}
|
|
|
|
static inline int64_t getCurNS(void)
|
|
{
|
|
int64_t ns;
|
|
struct timespec time;
|
|
|
|
time.tv_sec = time.tv_nsec = 0;
|
|
get_monotonic_boottime(&time);
|
|
ns = time.tv_sec * 1000000000LL + time.tv_nsec;
|
|
|
|
return ns;
|
|
}
|
|
|
|
/* OIS/EIS Timer & Workqueue */
|
|
static void ois_pos_polling(struct work_struct *data)
|
|
{
|
|
mutex_lock(&ois_mutex);
|
|
if (g_pstAF_CurDrv) {
|
|
if (g_pstAF_CurDrv->pAF_OisGetHallPos) {
|
|
int PosX = 0, PosY = 0;
|
|
|
|
g_pstAF_CurDrv->pAF_OisGetHallPos(&PosX, &PosY);
|
|
if (g_OisPosIdx >= 0) {
|
|
OisPosInfo.TimeStamp[g_OisPosIdx] = getCurNS();
|
|
OisPosInfo.i4OISHallPosX[g_OisPosIdx] = PosX;
|
|
OisPosInfo.i4OISHallPosY[g_OisPosIdx] = PosY;
|
|
g_OisPosIdx++;
|
|
g_OisPosIdx &= OIS_DATA_MASK;
|
|
}
|
|
}
|
|
}
|
|
mutex_unlock(&ois_mutex);
|
|
}
|
|
|
|
static enum hrtimer_restart ois_timer_func(struct hrtimer *timer)
|
|
{
|
|
g_GetOisInfoCnt--;
|
|
|
|
if (ois_workqueue != NULL && g_GetOisInfoCnt > 11)
|
|
queue_work(ois_workqueue, &ois_work);
|
|
|
|
if (g_GetOisInfoCnt < 10) {
|
|
g_EnableTimer = 0;
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
hrtimer_forward_now(timer, ktime_set(0, 5000000));
|
|
return HRTIMER_RESTART;
|
|
}
|
|
/* ------------------------- */
|
|
|
|
/* ////////////////////////////////////////////////////////////// */
|
|
static long AF_Ioctl(struct file *a_pstFile, unsigned int a_u4Command,
|
|
unsigned long a_u4Param)
|
|
{
|
|
long i4RetValue = 0;
|
|
|
|
switch (a_u4Command) {
|
|
case AFIOC_S_SETDRVNAME:
|
|
i4RetValue = AF_SetMotorName(
|
|
(__user struct stAF_MotorName *)(a_u4Param));
|
|
break;
|
|
|
|
case AFIOC_G_GETDRVNAME:
|
|
{
|
|
/* Set Driver Name */
|
|
int i;
|
|
struct stAF_MotorName stMotorName;
|
|
struct stAF_DrvList *pstAF_CurDrv = NULL;
|
|
__user struct stAF_MotorName *pstMotorName =
|
|
(__user struct stAF_MotorName *)a_u4Param;
|
|
|
|
if (copy_from_user(&stMotorName, pstMotorName,
|
|
sizeof(struct stAF_MotorName)))
|
|
LOG_INF("copy to user failed when getting motor information\n");
|
|
|
|
stMotorName.uMotorName[sizeof(stMotorName.uMotorName) - 1] = '\0';
|
|
|
|
LOG_INF("GETDRVNAME : set driver name(%s)\n", stMotorName.uMotorName);
|
|
|
|
for (i = 0; i < MAX_NUM_OF_LENS; i++) {
|
|
if (g_stAF_DrvList[i].uEnable != 1)
|
|
break;
|
|
|
|
LOG_INF("Search Motor Name : %s\n", g_stAF_DrvList[i].uDrvName);
|
|
if (strcmp(stMotorName.uMotorName,
|
|
g_stAF_DrvList[i].uDrvName) == 0) {
|
|
LOG_INF("Motor Name : %s\n", stMotorName.uMotorName);
|
|
pstAF_CurDrv = &g_stAF_DrvList[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get File Name */
|
|
if (pstAF_CurDrv) {
|
|
if (pstAF_CurDrv->pAF_GetFileName) {
|
|
__user struct stAF_MotorName *pstMotorName =
|
|
(__user struct stAF_MotorName *)a_u4Param;
|
|
struct stAF_MotorName MotorFileName;
|
|
|
|
pstAF_CurDrv->pAF_GetFileName(
|
|
MotorFileName.uMotorName);
|
|
i4RetValue = 1;
|
|
LOG_INF("GETDRVNAME : get file name(%s)\n",
|
|
MotorFileName.uMotorName);
|
|
if (copy_to_user(
|
|
pstMotorName, &MotorFileName,
|
|
sizeof(struct stAF_MotorName)))
|
|
LOG_INF("copy to user failed\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AFIOC_S_SETDRVINIT:
|
|
spin_lock(&g_AF_SpinLock);
|
|
g_s4AF_Opened = 1;
|
|
spin_unlock(&g_AF_SpinLock);
|
|
break;
|
|
|
|
case AFIOC_S_SETPOWERDOWN:
|
|
AF_PowerDown();
|
|
i4RetValue = 1;
|
|
break;
|
|
|
|
case AFIOC_G_OISPOSINFO:
|
|
if (g_pstAF_CurDrv) {
|
|
if (g_pstAF_CurDrv->pAF_OisGetHallPos) {
|
|
__user struct stAF_OisPosInfo *pstOisPosInfo =
|
|
(__user struct stAF_OisPosInfo *)
|
|
a_u4Param;
|
|
|
|
mutex_lock(&ois_mutex);
|
|
|
|
if (copy_to_user(
|
|
pstOisPosInfo, &OisPosInfo,
|
|
sizeof(struct stAF_OisPosInfo)))
|
|
LOG_INF("copy to user failed\n");
|
|
|
|
g_OisPosIdx = 0;
|
|
g_GetOisInfoCnt = 100;
|
|
memset(&OisPosInfo, 0, sizeof(OisPosInfo));
|
|
mutex_unlock(&ois_mutex);
|
|
|
|
if (g_EnableTimer == 0) {
|
|
/* Start Timer */
|
|
hrtimer_start(&ois_timer,
|
|
ktime_set(0, 50000000),
|
|
HRTIMER_MODE_REL);
|
|
g_EnableTimer = 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AFIOC_X_CTRLPARA:
|
|
if (AF_ControlParam(a_u4Param) <= 0) {
|
|
if (g_pstAF_CurDrv)
|
|
i4RetValue = g_pstAF_CurDrv->pAF_Ioctl(
|
|
a_pstFile, a_u4Command, a_u4Param);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (g_pstAF_CurDrv) {
|
|
if (g_pstAF_CurDrv->pAF_Ioctl)
|
|
i4RetValue = g_pstAF_CurDrv->pAF_Ioctl(
|
|
a_pstFile, a_u4Command, a_u4Param);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return i4RetValue;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long AF_Ioctl_Compat(struct file *a_pstFile, unsigned int a_u4Command,
|
|
unsigned long a_u4Param)
|
|
{
|
|
long i4RetValue = 0;
|
|
|
|
i4RetValue = AF_Ioctl(a_pstFile, a_u4Command,
|
|
(unsigned long)compat_ptr(a_u4Param));
|
|
|
|
return i4RetValue;
|
|
}
|
|
#endif
|
|
|
|
/* Main jobs: */
|
|
/* 1.check for device-specified errors, device not ready. */
|
|
/* 2.Initialize the device if it is opened for the first time. */
|
|
/* 3.Update f_op pointer. */
|
|
/* 4.Fill data structures into private_data */
|
|
/* CAM_RESET */
|
|
static int AF_Open(struct inode *a_pstInode, struct file *a_pstFile)
|
|
{
|
|
LOG_INF("Start\n");
|
|
|
|
spin_lock(&g_AF_SpinLock);
|
|
if (g_s4AF_Opened) {
|
|
spin_unlock(&g_AF_SpinLock);
|
|
LOG_INF("The device is opened\n");
|
|
return -EBUSY;
|
|
}
|
|
g_s4AF_Opened = 1;
|
|
spin_unlock(&g_AF_SpinLock);
|
|
|
|
camaf_power_init();
|
|
camaf_power_on();
|
|
|
|
/* OIS/EIS Timer & Workqueue */
|
|
/* init work queue */
|
|
INIT_WORK(&ois_work, ois_pos_polling);
|
|
|
|
/* init timer */
|
|
hrtimer_init(&ois_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
ois_timer.function = ois_timer_func;
|
|
|
|
g_EnableTimer = 0;
|
|
/* ------------------------- */
|
|
|
|
LOG_INF("End\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Main jobs: */
|
|
/* 1.Deallocate anything that "open" allocated in private_data. */
|
|
/* 2.Shut down the device on last close. */
|
|
/* 3.Only called once on last time. */
|
|
/* Q1 : Try release multiple times. */
|
|
static int AF_Release(struct inode *a_pstInode, struct file *a_pstFile)
|
|
{
|
|
LOG_INF("Start\n");
|
|
|
|
if (g_pstAF_CurDrv) {
|
|
g_pstAF_CurDrv->pAF_Release(a_pstInode, a_pstFile);
|
|
g_pstAF_CurDrv = NULL;
|
|
} else {
|
|
spin_lock(&g_AF_SpinLock);
|
|
g_s4AF_Opened = 0;
|
|
spin_unlock(&g_AF_SpinLock);
|
|
}
|
|
|
|
camaf_power_off();
|
|
|
|
/* OIS/EIS Timer & Workqueue */
|
|
/* Cancel Timer */
|
|
hrtimer_cancel(&ois_timer);
|
|
|
|
/* flush work queue */
|
|
flush_work(&ois_work);
|
|
|
|
if (ois_workqueue) {
|
|
flush_workqueue(ois_workqueue);
|
|
destroy_workqueue(ois_workqueue);
|
|
ois_workqueue = NULL;
|
|
}
|
|
/* ------------------------- */
|
|
|
|
LOG_INF("End\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations g_stAF_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = AF_Open,
|
|
.release = AF_Release,
|
|
.unlocked_ioctl = AF_Ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = AF_Ioctl_Compat,
|
|
#endif
|
|
};
|
|
|
|
static inline int Register_AF_CharDrv(void)
|
|
{
|
|
LOG_INF("Start\n");
|
|
|
|
/* Allocate char driver no. */
|
|
if (alloc_chrdev_region(&g_AF_devno, 0, 1, AF_DRVNAME)) {
|
|
LOG_INF("Allocate device no failed\n");
|
|
|
|
return -EAGAIN;
|
|
}
|
|
/* Allocate driver */
|
|
g_pAF_CharDrv = cdev_alloc();
|
|
|
|
if (g_pAF_CharDrv == NULL) {
|
|
unregister_chrdev_region(g_AF_devno, 1);
|
|
|
|
LOG_INF("Allocate mem for kobject failed\n");
|
|
|
|
return -ENOMEM;
|
|
}
|
|
/* Attatch file operation. */
|
|
cdev_init(g_pAF_CharDrv, &g_stAF_fops);
|
|
|
|
g_pAF_CharDrv->owner = THIS_MODULE;
|
|
|
|
/* Add to system */
|
|
if (cdev_add(g_pAF_CharDrv, g_AF_devno, 1)) {
|
|
LOG_INF("Attatch file operation failed\n");
|
|
|
|
unregister_chrdev_region(g_AF_devno, 1);
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
actuator_class = class_create(THIS_MODULE, AF_DRIVER_CLASS_NAME);
|
|
if (IS_ERR(actuator_class)) {
|
|
int ret = PTR_ERR(actuator_class);
|
|
|
|
LOG_INF("Unable to create class, err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
lens_device = device_create(actuator_class, NULL, g_AF_devno, NULL,
|
|
AF_DRVNAME);
|
|
|
|
if (lens_device == NULL)
|
|
return -EIO;
|
|
|
|
LOG_INF("End\n");
|
|
return 0;
|
|
}
|
|
|
|
static inline void Unregister_AF_CharDrv(void)
|
|
{
|
|
LOG_INF("Start\n");
|
|
|
|
/* Release char driver */
|
|
cdev_del(g_pAF_CharDrv);
|
|
|
|
unregister_chrdev_region(g_AF_devno, 1);
|
|
|
|
device_destroy(actuator_class, g_AF_devno);
|
|
|
|
class_destroy(actuator_class);
|
|
|
|
LOG_INF("End\n");
|
|
}
|
|
|
|
/* //////////////////////////////////////////////////////////////////// */
|
|
|
|
static int AF_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id);
|
|
static int AF_i2c_remove(struct i2c_client *client);
|
|
static const struct i2c_device_id AF_i2c_id[] = {{AF_DRVNAME, 0}, {} };
|
|
|
|
/* TOOL : kernel-3.10\tools\dct */
|
|
/* PATH : vendor\mediatek\proprietary\custom\#project#\kernel\dct\dct */
|
|
#if I2C_CONFIG_SETTING == 2
|
|
static const struct of_device_id MAINAF_of_match[] = {
|
|
{.compatible = "mediatek,CAMERA_MAIN_AF"}, {},
|
|
};
|
|
#endif
|
|
|
|
static struct i2c_driver AF_i2c_driver = {
|
|
.probe = AF_i2c_probe,
|
|
.remove = AF_i2c_remove,
|
|
.driver.name = AF_DRVNAME,
|
|
#if I2C_CONFIG_SETTING == 2
|
|
.driver.of_match_table = MAINAF_of_match,
|
|
#endif
|
|
.id_table = AF_i2c_id,
|
|
};
|
|
|
|
static int AF_i2c_remove(struct i2c_client *client)
|
|
{
|
|
Unregister_AF_CharDrv();
|
|
return 0;
|
|
}
|
|
|
|
/* Kirby: add new-style driver {*/
|
|
static int AF_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int i4RetValue = 0;
|
|
|
|
LOG_INF("Start\n");
|
|
|
|
/* Kirby: add new-style driver { */
|
|
g_pstAF_I2Cclient = client;
|
|
|
|
/* Register char driver */
|
|
i4RetValue = Register_AF_CharDrv();
|
|
|
|
if (i4RetValue) {
|
|
|
|
LOG_INF(" register char device failed!\n");
|
|
|
|
return i4RetValue;
|
|
}
|
|
|
|
spin_lock_init(&g_AF_SpinLock);
|
|
|
|
LOG_INF("Attached!!\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int AF_probe(struct platform_device *pdev)
|
|
{
|
|
return i2c_add_driver(&AF_i2c_driver);
|
|
}
|
|
|
|
static int AF_remove(struct platform_device *pdev)
|
|
{
|
|
i2c_del_driver(&AF_i2c_driver);
|
|
return 0;
|
|
}
|
|
|
|
static int AF_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int AF_resume(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id gaf_of_device_id[] = {
|
|
{.compatible = "mediatek,camera_af_lens",},
|
|
{}
|
|
};
|
|
#endif
|
|
|
|
/* platform structure */
|
|
static struct platform_driver g_stAF_Driver = {
|
|
.probe = AF_probe,
|
|
.remove = AF_remove,
|
|
.suspend = AF_suspend,
|
|
.resume = AF_resume,
|
|
.driver = {
|
|
.name = PLATFORM_DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = gaf_of_device_id,
|
|
#endif
|
|
} };
|
|
|
|
static struct platform_device g_stAF_device = {
|
|
.name = PLATFORM_DRIVER_NAME, .id = 0, .dev = {} };
|
|
|
|
static int __init MAINAF_i2C_init(void)
|
|
{
|
|
#if I2C_CONFIG_SETTING == 1
|
|
i2c_register_board_info(LENS_I2C_BUSNUM, &kd_lens_dev, 1);
|
|
#endif
|
|
|
|
if (platform_device_register(&g_stAF_device)) {
|
|
LOG_INF("failed to register AF driver\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (platform_driver_register(&g_stAF_Driver)) {
|
|
LOG_INF("Failed to register AF driver\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit MAINAF_i2C_exit(void)
|
|
{
|
|
platform_driver_unregister(&g_stAF_Driver);
|
|
platform_device_unregister(&g_stAF_device);
|
|
}
|
|
module_init(MAINAF_i2C_init);
|
|
module_exit(MAINAF_i2C_exit);
|
|
|
|
MODULE_DESCRIPTION("MAINAF lens module driver");
|
|
MODULE_AUTHOR("KY Chen <ky.chen@Mediatek.com>");
|
|
MODULE_LICENSE("GPL");
|