blob: 27f551876f59e12eedd01242fb7d96576f8e8d3e [file] [log] [blame]
/* Copyright (c) 2017, 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/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include "cam_subdev.h"
#include "cam_node.h"
#include "cam_fd_context.h"
#include "cam_fd_hw_mgr.h"
#include "cam_fd_hw_mgr_intf.h"
#define CAM_FD_DEV_NAME "cam-fd"
/**
* struct cam_fd_dev - FD device information
*
* @sd: Subdev information
* @base_ctx: List of base contexts
* @fd_ctx: List of FD contexts
* @lock: Mutex handle
* @open_cnt: FD subdev open count
* @probe_done: Whether FD probe is completed
*/
struct cam_fd_dev {
struct cam_subdev sd;
struct cam_context base_ctx[CAM_CTX_MAX];
struct cam_fd_context fd_ctx[CAM_CTX_MAX];
struct mutex lock;
uint32_t open_cnt;
bool probe_done;
};
static struct cam_fd_dev g_fd_dev;
static int cam_fd_dev_open(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct cam_fd_dev *fd_dev = &g_fd_dev;
if (!fd_dev->probe_done) {
CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
return -ENODEV;
}
mutex_lock(&fd_dev->lock);
fd_dev->open_cnt++;
CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
mutex_unlock(&fd_dev->lock);
return 0;
}
static int cam_fd_dev_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh)
{
struct cam_fd_dev *fd_dev = &g_fd_dev;
if (!fd_dev->probe_done) {
CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
return -ENODEV;
}
mutex_lock(&fd_dev->lock);
fd_dev->open_cnt--;
CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
mutex_unlock(&fd_dev->lock);
return 0;
}
static const struct v4l2_subdev_internal_ops cam_fd_subdev_internal_ops = {
.open = cam_fd_dev_open,
.close = cam_fd_dev_close,
};
static int cam_fd_dev_probe(struct platform_device *pdev)
{
int rc;
int i;
struct cam_hw_mgr_intf hw_mgr_intf;
struct cam_node *node;
g_fd_dev.sd.internal_ops = &cam_fd_subdev_internal_ops;
/* Initialze the v4l2 subdevice first. (create cam_node) */
rc = cam_subdev_probe(&g_fd_dev.sd, pdev, CAM_FD_DEV_NAME,
CAM_FD_DEVICE_TYPE);
if (rc) {
CAM_ERR(CAM_FD, "FD cam_subdev_probe failed, rc=%d", rc);
return rc;
}
node = (struct cam_node *) g_fd_dev.sd.token;
rc = cam_fd_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf);
if (rc) {
CAM_ERR(CAM_FD, "Failed in initializing FD HW manager, rc=%d",
rc);
goto unregister_subdev;
}
for (i = 0; i < CAM_CTX_MAX; i++) {
rc = cam_fd_context_init(&g_fd_dev.fd_ctx[i],
&g_fd_dev.base_ctx[i], &node->hw_mgr_intf);
if (rc) {
CAM_ERR(CAM_FD, "FD context init failed i=%d, rc=%d",
i, rc);
goto deinit_ctx;
}
}
rc = cam_node_init(node, &hw_mgr_intf, g_fd_dev.base_ctx, CAM_CTX_MAX,
CAM_FD_DEV_NAME);
if (rc) {
CAM_ERR(CAM_FD, "FD node init failed, rc=%d", rc);
goto deinit_ctx;
}
mutex_init(&g_fd_dev.lock);
g_fd_dev.probe_done = true;
CAM_DBG(CAM_FD, "Camera FD probe complete");
return 0;
deinit_ctx:
for (--i; i >= 0; i--) {
if (cam_fd_context_deinit(&g_fd_dev.fd_ctx[i]))
CAM_ERR(CAM_FD, "FD context %d deinit failed", i);
}
unregister_subdev:
if (cam_subdev_remove(&g_fd_dev.sd))
CAM_ERR(CAM_FD, "Failed in subdev remove");
return rc;
}
static int cam_fd_dev_remove(struct platform_device *pdev)
{
int i, rc;
for (i = 0; i < CAM_CTX_MAX; i++) {
rc = cam_fd_context_deinit(&g_fd_dev.fd_ctx[i]);
if (rc)
CAM_ERR(CAM_FD, "FD context %d deinit failed, rc=%d",
i, rc);
}
rc = cam_fd_hw_mgr_deinit(pdev->dev.of_node);
if (rc)
CAM_ERR(CAM_FD, "Failed in hw mgr deinit, rc=%d", rc);
rc = cam_subdev_remove(&g_fd_dev.sd);
if (rc)
CAM_ERR(CAM_FD, "Unregister failed, rc=%d", rc);
mutex_destroy(&g_fd_dev.lock);
g_fd_dev.probe_done = false;
return rc;
}
static const struct of_device_id cam_fd_dt_match[] = {
{
.compatible = "qcom,cam-fd"
},
{}
};
static struct platform_driver cam_fd_driver = {
.probe = cam_fd_dev_probe,
.remove = cam_fd_dev_remove,
.driver = {
.name = "cam_fd",
.owner = THIS_MODULE,
.of_match_table = cam_fd_dt_match,
},
};
static int __init cam_fd_dev_init_module(void)
{
return platform_driver_register(&cam_fd_driver);
}
static void __exit cam_fd_dev_exit_module(void)
{
platform_driver_unregister(&cam_fd_driver);
}
module_init(cam_fd_dev_init_module);
module_exit(cam_fd_dev_exit_module);
MODULE_DESCRIPTION("MSM FD driver");
MODULE_LICENSE("GPL v2");