/* * VI5 driver for T194 * * Copyright (c) 2017-2020, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vi5.h" #include "dev.h" #include "bus_client.h" #include "nvhost_acm.h" #include "capture/capture-support.h" #include "t194/t194.h" #include #include #include #include "camera/vi/vi5_fops.h" #include #include /* HW capability, pixels per clock */ #define NUM_PPC 8 /* 15% bus protocol overhead */ /* + 5% SW overhead */ #define VI_OVERHEAD 20 struct host_vi5 { struct platform_device *pdev; struct platform_device *vi_thi; struct vi vi_common; /* RCE RM area */ struct sg_table rm_sgt; /* Debugfs */ struct vi5_debug { struct debugfs_regset32 ch0; } debug; }; static int vi5_init_debugfs(struct host_vi5 *vi5); static void vi5_remove_debugfs(struct host_vi5 *vi5); static int vi5_alloc_syncpt(struct platform_device *pdev, const char *name, uint32_t *syncpt_id) { struct host_vi5 *vi5 = nvhost_get_private_data(pdev); return t194_capture_alloc_syncpt(vi5->vi_thi, name, syncpt_id); } int nvhost_vi5_aggregate_constraints(struct platform_device *dev, int clk_index, unsigned long floor_rate, unsigned long pixelrate, unsigned long bw_constraint) { struct nvhost_device_data *pdata = nvhost_get_devdata(dev); if (!pdata) { dev_err(&dev->dev, "No platform data, fall back to default policy\n"); return 0; } if (!pixelrate || clk_index != 0) return 0; /* SCF send request using NVHOST_CLK, which is calculated * in floor_rate, so we need to aggregate its request * with V4L2 pixelrate request */ return floor_rate + (pixelrate / pdata->num_ppc); } static void vi5_release_syncpt(struct platform_device *pdev, uint32_t id) { struct host_vi5 *vi5 = nvhost_get_private_data(pdev); t194_capture_release_syncpt(vi5->vi_thi, id); } static void vi5_get_gos_table(struct platform_device *pdev, int *count, const dma_addr_t **table) { struct host_vi5 *vi5 = nvhost_get_private_data(pdev); t194_capture_get_gos_table(vi5->vi_thi, count, table); } static int vi5_get_syncpt_gos_backing(struct platform_device *pdev, uint32_t id, dma_addr_t *syncpt_addr, uint32_t *gos_index, uint32_t *gos_offset) { struct host_vi5 *vi5 = nvhost_get_private_data(pdev); return t194_capture_get_syncpt_gos_backing(vi5->vi_thi, id, syncpt_addr, gos_index, gos_offset); } static struct vi_channel_drv_ops vi5_channel_drv_ops = { .alloc_syncpt = vi5_alloc_syncpt, .release_syncpt = vi5_release_syncpt, .get_gos_table = vi5_get_gos_table, .get_syncpt_gos_backing = vi5_get_syncpt_gos_backing, }; int vi5_priv_early_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct nvhost_device_data *info; struct device_node *thi_np; struct platform_device *thi = NULL; struct host_vi5 *vi5; int err = 0; unsigned int num_channels = 0; info = (void *)of_device_get_match_data(dev); if (unlikely(info == NULL)) { dev_WARN(dev, "no platform data\n"); return -ENODATA; } thi_np = of_parse_phandle(dev->of_node, "nvidia,vi-falcon-device", 0); if (thi_np == NULL) { dev_WARN(dev, "missing %s handle\n", "nvidia,vi-falcon-device"); return -ENODEV; } thi = of_find_device_by_node(thi_np); of_node_put(thi_np); if (thi == NULL) return -ENODEV; if (thi->dev.driver == NULL) { err = -EPROBE_DEFER; goto put_vi; } if (of_property_read_u32(dev->of_node, "nvidia,num-vi-channels", &num_channels)) { dev_warn(dev, "using default number of vi channels, %d\n", info->num_channels); } else { if (num_channels < info->num_channels) { info->num_channels = num_channels; } else { dev_WARN(dev, "num of channels are out of range, use default num"); } } dev_dbg(dev, "num vi channels : %d\n", info->num_channels); vi5 = (struct host_vi5*) devm_kzalloc(dev, sizeof(*vi5), GFP_KERNEL); if (!vi5) { err = -ENOMEM; goto put_vi; } vi5->vi_thi = thi; vi5->pdev = pdev; info->pdev = pdev; mutex_init(&info->lock); platform_set_drvdata(pdev, info); info->private_data = vi5; (void) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); return 0; put_vi: platform_device_put(thi); if (err != -EPROBE_DEFER) dev_err(&pdev->dev, "probe failed: %d\n", err); info->private_data = NULL; return err; } int vi5_priv_late_probe(struct platform_device *pdev) { struct tegra_camera_dev_info vi_info; struct nvhost_device_data *info = platform_get_drvdata(pdev); struct host_vi5 *vi5 = info->private_data; int err; memset(&vi_info, 0, sizeof(vi_info)); vi_info.pdev = pdev; vi_info.hw_type = HWTYPE_VI; vi_info.ppc = NUM_PPC; vi_info.overhead = VI_OVERHEAD; err = tegra_camera_device_register(&vi_info, vi5); if (err) goto device_release; err = vi_channel_drv_register(pdev, &vi5_channel_drv_ops); if (err) goto device_release; vi5_init_debugfs(vi5); vi5->vi_common.mc_vi.vi = &vi5->vi_common; vi5->vi_common.mc_vi.fops = &vi5_fops; err = tegra_vi_media_controller_init(&vi5->vi_common.mc_vi, pdev); if (err) { dev_warn(&pdev->dev, "media controller init failed\n"); err = 0; } return 0; device_release: nvhost_client_device_release(pdev); return err; } static int vi5_probe(struct platform_device *pdev) { int err; struct nvhost_device_data *pdata; struct host_vi5 *vi5; err = vi5_priv_early_probe(pdev); if (err) goto error; pdata = platform_get_drvdata(pdev); vi5 = pdata->private_data; err = nvhost_client_device_get_resources(pdev); if (err) goto put_vi; err = nvhost_module_init(pdev); if (err) goto put_vi; err = nvhost_client_device_init(pdev); if (err) goto deinit; vi5_priv_late_probe(pdev); return 0; deinit: nvhost_module_deinit(pdev); put_vi: platform_device_put(vi5->vi_thi); if (err != -EPROBE_DEFER) dev_err(&pdev->dev, "probe failed: %d\n", err); error: return err; } struct t194_vi5_file_private { struct platform_device *pdev; struct tegra_mc_vi mc_vi; unsigned int vi_bypass_bw; }; static int vi5_remove(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct host_vi5 *vi5 = pdata->private_data; tegra_camera_device_unregister(vi5); vi_channel_drv_unregister(&pdev->dev); tegra_vi_media_controller_cleanup(&vi5->vi_common.mc_vi); if (vi5->rm_sgt.sgl) { dma_unmap_sg(&pdev->dev, vi5->rm_sgt.sgl, vi5->rm_sgt.orig_nents, DMA_FROM_DEVICE); sg_free_table(&vi5->rm_sgt); } vi5_remove_debugfs(vi5); platform_device_put(vi5->vi_thi); return 0; } static const struct of_device_id tegra_vi5_of_match[] = { { .compatible = "nvidia,tegra194-vi", .data = &t19_vi5_info, }, { }, }; static struct platform_driver vi5_driver = { .probe = vi5_probe, .remove = vi5_remove, .driver = { .owner = THIS_MODULE, .name = "tegra194-vi5", #ifdef CONFIG_OF .of_match_table = tegra_vi5_of_match, #endif #ifdef CONFIG_PM .pm = &nvhost_module_pm_ops, #endif }, }; module_platform_driver(vi5_driver); /* === Debugfs ========================================================== */ static int vi5_init_debugfs(struct host_vi5 *vi5) { static const struct debugfs_reg32 vi5_ch_regs[] = { { .name = "protocol_version", 0x00 }, { .name = "perforce_changelist", 0x4 }, { .name = "build_timestamp", 0x8 }, { .name = "channel_count", 0x80 }, }; struct nvhost_device_data *pdata = platform_get_drvdata(vi5->pdev); struct dentry *dir = pdata->debugfs; struct vi5_debug *debug = &vi5->debug; debug->ch0.base = pdata->aperture[0]; debug->ch0.regs = vi5_ch_regs; debug->ch0.nregs = ARRAY_SIZE(vi5_ch_regs); debugfs_create_regset32("ch0", S_IRUGO, dir, &debug->ch0); return 0; } static void vi5_remove_debugfs(struct host_vi5 *vi5) { }