Files
android_kernel_samsung_sm86…/driver/vidc/src/msm_vidc_probe.c
Govindaraj Rajagopal 0d72a68a7f video: driver: add video banner support
Video-banner contains compilation timestamp, which will
be helpful in stability debugging to find mismatch b/w
ramdump and symbols.

Change-Id: Ibc446fc4398a184b840867c568ed9e8940e490ae
Signed-off-by: Govindaraj Rajagopal <quic_grajagop@quicinc.com>
2022-03-21 19:20:43 +05:30

725 righe
17 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2022, The Linux Foundation. All rights reserved.
*/
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/interrupt.h>
#include "msm_vidc_internal.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_driver.h"
#include "msm_vidc_dt.h"
#include "msm_vidc_platform.h"
#include "msm_vidc_core.h"
#include "venus_hfi.h"
#include "video_generated_h"
#define BASE_DEVICE_NUMBER 32
struct msm_vidc_core *g_core;
const char video_banner[] = "Video-Banner: (" VIDEO_COMPILE_BY "@"
VIDEO_COMPILE_HOST ") (" VIDEO_COMPILE_TIME ")";
static int msm_vidc_deinit_irq(struct msm_vidc_core *core)
{
struct msm_vidc_dt *dt;
if (!core || !core->pdev || !core->dt) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
dt = core->dt;
d_vpr_h("%s: reg_base = %pa, reg_size = %#x\n",
__func__, &dt->register_base, dt->register_size);
dt->irq = 0;
if (core->register_base_addr)
devm_iounmap(&core->pdev->dev, core->register_base_addr);
core->register_base_addr = 0;
return 0;
}
static int msm_vidc_init_irq(struct msm_vidc_core *core)
{
int rc = 0;
struct msm_vidc_dt *dt;
if (!core || !core->pdev || !core->dt) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
dt = core->dt;
core->register_base_addr = devm_ioremap(&core->pdev->dev,
dt->register_base, dt->register_size);
if (!core->register_base_addr) {
d_vpr_e("could not map reg addr %pa of size %d\n",
&dt->register_base, dt->register_size);
rc = -EINVAL;
goto exit;
}
rc = devm_request_threaded_irq(&core->pdev->dev, dt->irq, venus_hfi_isr,
venus_hfi_isr_handler, IRQF_TRIGGER_HIGH, "msm-vidc", core);
if (rc) {
d_vpr_e("%s: Failed to allocate venus IRQ\n", __func__);
goto exit;
}
disable_irq_nosync(dt->irq);
d_vpr_h("%s: reg_base = %pa, reg_size = %d\n",
__func__, &dt->register_base, dt->register_size);
return 0;
exit:
msm_vidc_deinit_irq(core);
return rc;
}
static ssize_t sku_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct msm_vidc_core *core;
/*
* Default sku version: 0
* driver possibly not probed yet or not the main device.
*/
if (!dev || !dev->driver ||
!of_device_is_compatible(dev->of_node, "qcom,msm-vidc"))
return 0;
core = dev_get_drvdata(dev);
if (!core || !core->platform) {
d_vpr_e("%s: invalid core\n", __func__);
return 0;
}
return scnprintf(buf, PAGE_SIZE, "%d",
core->platform->data.sku_version);
}
static DEVICE_ATTR_RO(sku_version);
static struct attribute *msm_vidc_core_attrs[] = {
&dev_attr_sku_version.attr,
NULL
};
static struct attribute_group msm_vidc_core_attr_group = {
.attrs = msm_vidc_core_attrs,
};
static const struct of_device_id msm_vidc_dt_match[] = {
{.compatible = "qcom,msm-vidc"},
{.compatible = "qcom,msm-vidc,context-bank"},
MSM_VIDC_EMPTY_BRACE
};
MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
static void msm_vidc_release_video_device(struct video_device *vdev)
{
d_vpr_e("%s:\n", __func__);
}
static void msm_vidc_unregister_video_device(struct msm_vidc_core *core,
enum msm_vidc_domain_type type)
{
int index;
d_vpr_h("%s()\n", __func__);
if (type == MSM_VIDC_DECODER)
index = 0;
else if (type == MSM_VIDC_ENCODER)
index = 1;
else
return;
#ifdef CONFIG_MEDIA_CONTROLLER
v4l2_m2m_unregister_media_controller(core->vdev[index].m2m_dev);
v4l2_m2m_release(core->vdev[index].m2m_dev);
#endif
//rc = device_create_file(&core->vdev[index].vdev.dev, &dev_attr_link_name);
video_set_drvdata(&core->vdev[index].vdev, NULL);
video_unregister_device(&core->vdev[index].vdev);
//memset vdev to 0
}
static int msm_vidc_register_video_device(struct msm_vidc_core *core,
enum msm_vidc_domain_type type, int nr)
{
int rc = 0;
int index, media_index;
d_vpr_h("%s()\n", __func__);
if (type == MSM_VIDC_DECODER) {
index = 0;
media_index = MEDIA_ENT_F_PROC_VIDEO_DECODER;
} else if (type == MSM_VIDC_ENCODER) {
index = 1;
media_index = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
} else {
return -EINVAL;
}
core->vdev[index].vdev.release =
msm_vidc_release_video_device;
core->vdev[index].vdev.fops = core->v4l2_file_ops;
if (type == MSM_VIDC_DECODER)
core->vdev[index].vdev.ioctl_ops = core->v4l2_ioctl_ops_dec;
else
core->vdev[index].vdev.ioctl_ops = core->v4l2_ioctl_ops_enc;
core->vdev[index].vdev.vfl_dir = VFL_DIR_M2M;
core->vdev[index].type = type;
core->vdev[index].vdev.v4l2_dev = &core->v4l2_dev;
core->vdev[index].vdev.device_caps =
V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_META_CAPTURE |
V4L2_CAP_STREAMING;
rc = video_register_device(&core->vdev[index].vdev,
VFL_TYPE_VIDEO, nr);
if (rc) {
d_vpr_e("Failed to register the video device\n");
return rc;
}
video_set_drvdata(&core->vdev[index].vdev, core);
//rc = device_create_file(&core->vdev[index].vdev.dev, &dev_attr_link_name);
if (rc) {
d_vpr_e("Failed to create video device file\n");
goto video_reg_failed;
}
#ifdef CONFIG_MEDIA_CONTROLLER
core->vdev[index].m2m_dev = v4l2_m2m_init(core->v4l2_m2m_ops);
if (IS_ERR(core->vdev[index].m2m_dev)) {
d_vpr_e("Failed to initialize V4L2 M2M device\n");
rc = PTR_ERR(core->vdev[index].m2m_dev);
goto m2m_init_failed;
}
rc = v4l2_m2m_register_media_controller(core->vdev[index].m2m_dev,
&core->vdev[index].vdev, media_index);
if (rc) {
d_vpr_e("%s: m2m_dev controller register failed for session type %d\n",
__func__, index);
goto m2m_mc_failed;
}
#endif
return 0;
#ifdef CONFIG_MEDIA_CONTROLLER
m2m_mc_failed:
v4l2_m2m_release(core->vdev[index].m2m_dev);
m2m_init_failed:
#endif
video_reg_failed:
video_unregister_device(&core->vdev[index].vdev);
return rc;
}
static int msm_vidc_check_mmrm_support(struct msm_vidc_core *core)
{
int rc = 0;
if (!core || !core->capabilities) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
if (!core->capabilities[MMRM].value)
goto exit;
if (!mmrm_client_check_scaling_supported(MMRM_CLIENT_CLOCK, 0)) {
d_vpr_e("%s: MMRM not supported\n", __func__);
core->capabilities[MMRM].value = 0;
}
exit:
d_vpr_h("%s: %d\n", __func__, core->capabilities[MMRM].value);
return rc;
}
static int msm_vidc_deinitialize_core(struct msm_vidc_core *core)
{
int rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
d_vpr_h("%s()\n", __func__);
mutex_destroy(&core->lock);
msm_vidc_change_core_state(core, MSM_VIDC_CORE_DEINIT, __func__);
kfree(core->response_packet);
kfree(core->packet);
core->response_packet = NULL;
core->packet = NULL;
if (core->batch_workq)
destroy_workqueue(core->batch_workq);
if (core->pm_workq)
destroy_workqueue(core->pm_workq);
core->batch_workq = NULL;
core->pm_workq = NULL;
return rc;
}
static int msm_vidc_initialize_core(struct msm_vidc_core *core)
{
int rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
d_vpr_h("%s()\n", __func__);
msm_vidc_change_core_state(core, MSM_VIDC_CORE_DEINIT, __func__);
core->pm_workq = create_singlethread_workqueue("pm_workq");
if (!core->pm_workq) {
d_vpr_e("%s: create pm workq failed\n", __func__);
rc = -EINVAL;
goto exit;
}
core->batch_workq = create_singlethread_workqueue("batch_workq");
if (!core->batch_workq) {
d_vpr_e("%s: create batch workq failed\n", __func__);
rc = -EINVAL;
goto exit;
}
core->packet_size = 4096;
core->packet = kzalloc(core->packet_size, GFP_KERNEL);
if (!core->packet) {
d_vpr_e("%s(): core packet allocation failed\n", __func__);
rc = -ENOMEM;
goto exit;
}
core->response_packet = kzalloc(core->packet_size, GFP_KERNEL);
if (!core->response_packet) {
d_vpr_e("%s(): core response packet allocation failed\n",
__func__);
rc = -ENOMEM;
goto exit;
}
mutex_init(&core->lock);
INIT_LIST_HEAD(&core->instances);
INIT_LIST_HEAD(&core->dangling_instances);
INIT_DELAYED_WORK(&core->pm_work, venus_hfi_pm_work_handler);
INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler);
INIT_WORK(&core->ssr_work, msm_vidc_ssr_handler);
return 0;
exit:
kfree(core->response_packet);
kfree(core->packet);
core->response_packet = NULL;
core->packet = NULL;
if (core->batch_workq)
destroy_workqueue(core->batch_workq);
if (core->pm_workq)
destroy_workqueue(core->pm_workq);
core->batch_workq = NULL;
core->pm_workq = NULL;
return rc;
}
static int msm_vidc_remove_video_device(struct platform_device *pdev)
{
struct msm_vidc_core* core;
if (!pdev) {
d_vpr_e("%s: invalid input %pK", __func__, pdev);
return -EINVAL;
}
core = dev_get_drvdata(&pdev->dev);
if (!core) {
d_vpr_e("%s: invalid core", __func__);
return -EINVAL;
}
d_vpr_h("%s()\n", __func__);
msm_vidc_core_deinit(core, true);
d_vpr_h("depopulating sub devices\n");
/*
* Trigger remove for each sub-device i.e. qcom,msm-vidc,context-bank.
* When msm_vidc_remove is called for each sub-device, destroy
* context-bank mappings.
*/
of_platform_depopulate(&pdev->dev);
#ifdef CONFIG_MEDIA_CONTROLLER
media_device_unregister(&core->media_dev);
#endif
msm_vidc_unregister_video_device(core, MSM_VIDC_ENCODER);
msm_vidc_unregister_video_device(core, MSM_VIDC_DECODER);
//device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev,
//&dev_attr_link_name);
//device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev,
//&dev_attr_link_name);
#ifdef CONFIG_MEDIA_CONTROLLER
media_device_cleanup(&core->media_dev);
#endif
v4l2_device_unregister(&core->v4l2_dev);
sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
msm_vidc_deinit_instance_caps(core);
msm_vidc_deinit_core_caps(core);
msm_vidc_deinit_irq(core);
msm_vidc_deinit_platform(pdev);
msm_vidc_deinit_dt(pdev);
msm_vidc_deinitialize_core(core);
dev_set_drvdata(&pdev->dev, NULL);
debugfs_remove_recursive(core->debugfs_parent);
kfree(core);
g_core = NULL;
return 0;
}
static int msm_vidc_remove_context_bank(struct platform_device *pdev)
{
d_vpr_h("%s(): Detached %s and destroyed mapping\n",
__func__, dev_name(&pdev->dev));
return 0;
}
static int msm_vidc_remove(struct platform_device *pdev)
{
/*
* Sub devices remove will be triggered by of_platform_depopulate()
* after core_deinit(). It return immediately after completing
* sub-device remove.
*/
if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-vidc")) {
return msm_vidc_remove_video_device(pdev);
} else if (of_device_is_compatible(pdev->dev.of_node,
"qcom,msm-vidc,context-bank")) {
return msm_vidc_remove_context_bank(pdev);
}
/* How did we end up here? */
WARN_ON(1);
return -EINVAL;
}
static int msm_vidc_probe_video_device(struct platform_device *pdev)
{
int rc = 0;
struct msm_vidc_core *core;
int nr = BASE_DEVICE_NUMBER;
d_vpr_h("%s()\n", __func__);
core = kzalloc(sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
g_core = core;
core->debugfs_parent = msm_vidc_debugfs_init_drv();
if (!core->debugfs_parent)
d_vpr_h("Failed to create debugfs for msm_vidc\n");
core->pdev = pdev;
dev_set_drvdata(&pdev->dev, core);
rc = msm_vidc_initialize_core(core);
if (rc) {
d_vpr_e("%s: init core failed with %d\n", __func__, rc);
goto init_core_failed;
}
rc = msm_vidc_init_dt(pdev);
if (rc) {
d_vpr_e("%s: init dt failed with %d\n", __func__, rc);
rc = -EINVAL;
goto init_dt_failed;
}
rc = msm_vidc_init_platform(pdev);
if (rc) {
d_vpr_e("%s: init platform failed with %d\n", __func__, rc);
rc = -EINVAL;
goto init_plat_failed;
}
rc = msm_vidc_init_irq(core);
if (rc) {
d_vpr_e("%s: init irq failed with %d\n", __func__, rc);
goto init_irq_failed;
}
rc = msm_vidc_init_core_caps(core);
if (rc) {
d_vpr_e("%s: init core caps failed with %d\n", __func__, rc);
goto init_core_caps_fail;
}
rc = msm_vidc_init_instance_caps(core);
if (rc) {
d_vpr_e("%s: init inst cap failed with %d\n", __func__, rc);
goto init_inst_caps_fail;
}
rc = sysfs_create_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
if (rc) {
d_vpr_e("Failed to create attributes\n");
goto init_group_failed;
}
rc = v4l2_device_register(&pdev->dev, &core->v4l2_dev);
if (rc) {
d_vpr_e("Failed to register v4l2 device\n");
goto v4l2_reg_failed;
}
#ifdef CONFIG_MEDIA_CONTROLLER
core->media_dev.dev = &core->pdev->dev;
strscpy(core->media_dev.model, "msm_vidc_media", sizeof(core->media_dev.model));
media_device_init(&core->media_dev);
core->media_dev.ops = core->media_device_ops;
core->v4l2_dev.mdev = &core->media_dev;
#endif
/* setup the decoder device */
rc = msm_vidc_register_video_device(core, MSM_VIDC_DECODER, nr);
if (rc) {
d_vpr_e("Failed to register video decoder\n");
goto dec_reg_failed;
}
/* setup the encoder device */
rc = msm_vidc_register_video_device(core, MSM_VIDC_ENCODER, nr + 1);
if (rc) {
d_vpr_e("Failed to register video encoder\n");
goto enc_reg_failed;
}
#ifdef CONFIG_MEDIA_CONTROLLER
rc = media_device_register(&core->media_dev);
if (rc) {
d_vpr_e("%s: media_device_register failed with %d\n", __func__, rc);
goto media_reg_failed;
}
#endif
rc = msm_vidc_check_mmrm_support(core);
if (rc) {
d_vpr_e("Failed to check MMRM scaling support\n");
rc = 0; /* Ignore error */
}
core->debugfs_root = msm_vidc_debugfs_init_core(core);
if (!core->debugfs_root)
d_vpr_h("Failed to init debugfs core\n");
d_vpr_h("populating sub devices\n");
/*
* Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank.
* When msm_vidc_probe is called for each sub-device, parse the
* context-bank details and store it in core->resources.context_banks
* list.
*/
rc = of_platform_populate(pdev->dev.of_node, msm_vidc_dt_match, NULL,
&pdev->dev);
if (rc) {
d_vpr_e("Failed to trigger probe for sub-devices\n");
goto sub_dev_failed;
}
rc = msm_vidc_core_init(core);
if (rc) {
d_vpr_e("%s: sys init failed\n", __func__);
goto core_init_failed;
}
d_vpr_h("%s(): succssful\n", __func__);
return rc;
core_init_failed:
of_platform_depopulate(&pdev->dev);
sub_dev_failed:
#ifdef CONFIG_MEDIA_CONTROLLER
media_device_unregister(&core->media_dev);
media_reg_failed:
#endif
msm_vidc_unregister_video_device(core, MSM_VIDC_ENCODER);
enc_reg_failed:
msm_vidc_unregister_video_device(core, MSM_VIDC_DECODER);
dec_reg_failed:
v4l2_device_unregister(&core->v4l2_dev);
v4l2_reg_failed:
sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
init_group_failed:
msm_vidc_deinit_instance_caps(core);
init_inst_caps_fail:
msm_vidc_deinit_core_caps(core);
init_core_caps_fail:
msm_vidc_deinit_irq(core);
init_irq_failed:
msm_vidc_deinit_platform(pdev);
init_plat_failed:
msm_vidc_deinit_dt(pdev);
init_dt_failed:
msm_vidc_deinitialize_core(core);
init_core_failed:
dev_set_drvdata(&pdev->dev, NULL);
debugfs_remove_recursive(core->debugfs_parent);
kfree(core);
g_core = NULL;
return rc;
}
static int msm_vidc_probe_context_bank(struct platform_device *pdev)
{
d_vpr_h("%s()\n", __func__);
return msm_vidc_read_context_bank_resources_from_dt(pdev);
}
static int msm_vidc_probe(struct platform_device *pdev)
{
d_vpr_h("%s()\n", __func__);
/*
* Sub devices probe will be triggered by of_platform_populate() towards
* the end of the probe function after msm-vidc device probe is
* completed. Return immediately after completing sub-device probe.
*/
if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-vidc")) {
return msm_vidc_probe_video_device(pdev);
} else if (of_device_is_compatible(pdev->dev.of_node,
"qcom,msm-vidc,context-bank")) {
return msm_vidc_probe_context_bank(pdev);
}
/* How did we end up here? */
WARN_ON(1);
return -EINVAL;
}
static int msm_vidc_pm_suspend(struct device *dev)
{
int rc = 0;
struct msm_vidc_core *core;
/*
* Bail out if
* - driver possibly not probed yet
* - not the main device. We don't support power management on
* subdevices (e.g. context banks)
*/
if (!dev || !dev->driver ||
!of_device_is_compatible(dev->of_node, "qcom,msm-vidc"))
return 0;
core = dev_get_drvdata(dev);
if (!core) {
d_vpr_e("%s: invalid core\n", __func__);
return -EINVAL;
}
d_vpr_h("%s\n", __func__);
rc = msm_vidc_suspend(core);
if (rc == -ENOTSUPP)
rc = 0;
else if (rc)
d_vpr_e("Failed to suspend: %d\n", rc);
else
core->pm_suspended = true;
return rc;
}
static int msm_vidc_pm_resume(struct device *dev)
{
struct msm_vidc_core *core;
/*
* Bail out if
* - driver possibly not probed yet
* - not the main device. We don't support power management on
* subdevices (e.g. context banks)
*/
if (!dev || !dev->driver ||
!of_device_is_compatible(dev->of_node, "qcom,msm-vidc"))
return 0;
core = dev_get_drvdata(dev);
if (!core) {
d_vpr_e("%s: invalid core\n", __func__);
return -EINVAL;
}
d_vpr_h("%s\n", __func__);
core->pm_suspended = false;
return 0;
}
static const struct dev_pm_ops msm_vidc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_vidc_pm_suspend, msm_vidc_pm_resume)
};
struct platform_driver msm_vidc_driver = {
.probe = msm_vidc_probe,
.remove = msm_vidc_remove,
.driver = {
.name = "msm_vidc_v4l2",
.of_match_table = msm_vidc_dt_match,
.pm = &msm_vidc_pm_ops,
},
};
static int __init msm_vidc_init(void)
{
int rc = 0;
d_vpr_h("%s: %s\n", __func__, video_banner);
rc = platform_driver_register(&msm_vidc_driver);
if (rc) {
d_vpr_e("Failed to register platform driver\n");
return rc;
}
return 0;
}
static void __exit msm_vidc_exit(void)
{
d_vpr_h("%s()\n", __func__);
platform_driver_unregister(&msm_vidc_driver);
}
module_init(msm_vidc_init);
module_exit(msm_vidc_exit);
MODULE_SOFTDEP("pre: subsys-pil-tz msm-mmrm");
MODULE_LICENSE("GPL v2");