blob: af794bb78e1c25a43f950eb47dfa031b3739964a [file] [log] [blame]
/* Copyright (c) 2011-2014, 2016, 2018, 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/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include "msm_camera_i2c_mux.h"
/* TODO move this somewhere else */
#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
static int msm_i2c_mux_config(struct i2c_mux_device *mux_device, uint8_t *mode)
{
uint32_t val;
val = msm_camera_io_r(mux_device->ctl_base);
if (*mode == MODE_DUAL) {
msm_camera_io_w(val | 0x3, mux_device->ctl_base);
} else if (*mode == MODE_L) {
msm_camera_io_w(((val | 0x2) & ~(0x1)), mux_device->ctl_base);
val = msm_camera_io_r(mux_device->ctl_base);
CDBG("the camio mode config left value is %d\n", val);
} else {
msm_camera_io_w(((val | 0x1) & ~(0x2)), mux_device->ctl_base);
val = msm_camera_io_r(mux_device->ctl_base);
CDBG("the camio mode config right value is %d\n", val);
}
return 0;
}
static int msm_i2c_mux_init(struct i2c_mux_device *mux_device)
{
int rc = 0, val = 0;
if (mux_device->use_count == 0) {
val = msm_camera_io_r(mux_device->rw_base);
msm_camera_io_w((val | 0x200), mux_device->rw_base);
}
mux_device->use_count++;
return 0;
};
static int msm_i2c_mux_release(struct i2c_mux_device *mux_device)
{
int val = 0;
mux_device->use_count--;
if (mux_device->use_count == 0) {
val = msm_camera_io_r(mux_device->rw_base);
msm_camera_io_w((val & ~0x200), mux_device->rw_base);
}
return 0;
}
static long msm_i2c_mux_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct i2c_mux_device *mux_device;
int rc = 0;
mux_device = v4l2_get_subdevdata(sd);
if (mux_device == NULL) {
rc = -ENOMEM;
return rc;
}
mutex_lock(&mux_device->mutex);
switch (cmd) {
case VIDIOC_MSM_I2C_MUX_CFG:
rc = msm_i2c_mux_config(mux_device, (uint8_t *) arg);
break;
case VIDIOC_MSM_I2C_MUX_INIT:
rc = msm_i2c_mux_init(mux_device);
break;
case VIDIOC_MSM_I2C_MUX_RELEASE:
rc = msm_i2c_mux_release(mux_device);
break;
default:
rc = -ENOIOCTLCMD;
}
mutex_unlock(&mux_device->mutex);
return rc;
}
static struct v4l2_subdev_core_ops msm_i2c_mux_subdev_core_ops = {
.ioctl = &msm_i2c_mux_subdev_ioctl,
};
static const struct v4l2_subdev_ops msm_i2c_mux_subdev_ops = {
.core = &msm_i2c_mux_subdev_core_ops,
};
static int i2c_mux_probe(struct platform_device *pdev)
{
struct i2c_mux_device *mux_device;
int rc = 0;
CDBG("%s: device id = %d\n", __func__, pdev->id);
mux_device = kzalloc(sizeof(struct i2c_mux_device), GFP_KERNEL);
if (!mux_device) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&mux_device->subdev, &msm_i2c_mux_subdev_ops);
v4l2_set_subdevdata(&mux_device->subdev, mux_device);
platform_set_drvdata(pdev, &mux_device->subdev);
mutex_init(&mux_device->mutex);
mux_device->ctl_base = msm_camera_get_reg_base(pdev,
"i2c_mux_ctl", true);
if (!mux_device->ctl_base) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto ctl_base_failed;
}
mux_device->rw_base = msm_camera_get_reg_base(pdev, "i2c_mux_rw", true);
if (!mux_device->rw_mem) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto rw_base_failed;
}
mux_device->pdev = pdev;
return 0;
rw_base_failed:
msm_camera_put_reg_base(pdev, mux_device->ctl_base,
"i2c_mux_ctl", true);
ctl_base_failed:
mutex_destroy(&mux_device->mutex);
kfree(mux_device);
return 0;
}
static int i2c_mux_remove(struct platform_device *pdev)
{
struct v4l2_subdev *sub_dev = platform_get_drvdata(pdev);
struct i2c_mux_device *mux_device;
if (!sub_dev) {
pr_err("%s: sub device is NULL\n", __func__);
return 0;
}
mux_device = (struct mux_device *)v4l2_get_subdevdata(sub_dev);
if (!mux_device) {
pr_err("%s: sub device is NULL\n", __func__);
return 0;
}
msm_camera_put_reg_base(pdev, mux_device->rw_base, "i2c_mux_ctl", true);
msm_camera_put_reg_base(pdev, mux_device->ctl_base, "i2c_mux_rw", true);
}
static struct platform_driver i2c_mux_driver = {
.probe = i2c_mux_probe,
.remove = i2c_mux_remove,
.driver = {
.name = MSM_I2C_MUX_DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init msm_camera_i2c_mux_init_module(void)
{
return platform_driver_register(&i2c_mux_driver);
}
static void __exit msm_camera_i2c_mux_exit_module(void)
{
platform_driver_unregister(&i2c_mux_driver);
}
module_init(msm_camera_i2c_mux_init_module);
module_exit(msm_camera_i2c_mux_exit_module);
MODULE_DESCRIPTION("MSM Camera I2C mux driver");
MODULE_LICENSE("GPL v2");