| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/platform_device.h> |
| #include <linux/device.h> |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/miscdevice.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| #include <linux/of_address.h> |
| |
| #include "auto_write.h" |
| #include "auto_gdc.h" |
| |
| #define AUTOWR_MODULE_NAME "autowrite" |
| |
| #define AUTOWR_IOC_MAGIC 'W' |
| #define AUTOWR_GET_FRMINFO _IOWR(AUTOWR_IOC_MAGIC, \ |
| 0x00, struct autowr_frm_info) |
| #define AUTOWR_STOP_CAPTURE _IOWR(AUTOWR_IOC_MAGIC, \ |
| 0x01, struct autowr_frm_info) |
| #define AUTOWR_MMAP_FLAG _IOWR(AUTOWR_IOC_MAGIC, \ |
| 0x02, struct autowr_frmbuf_info) |
| #define AUTOWR_GDC_CONFIG _IOWR(AUTOWR_IOC_MAGIC, \ |
| 0x03, struct autowr_gdc_cfg) |
| |
| static struct autowr_dev_base autowr_dev; |
| |
| static void *autowr_get_devbase(void) |
| { |
| return (void *)&autowr_dev; |
| } |
| |
| static int autowr_fops_open(struct inode *p_inode, struct file *p_file) |
| { |
| s32 rtn = 0; |
| |
| p_file->private_data = autowr_get_devbase(); |
| |
| return rtn; |
| } |
| |
| static int autowr_fops_release(struct inode *p_inode, struct file *p_file) |
| { |
| s32 rtn = 0; |
| |
| p_file->private_data = NULL; |
| |
| return rtn; |
| } |
| |
| static long autowr_fops_ioctl(struct file *p_file, u32 cmd, unsigned long arg) |
| { |
| long rtn = 0; |
| void __user *argp = (void __user *)arg; |
| struct autowr_frm_info frm_info; |
| struct autowr_frmbuf_info frmbuf_info; |
| struct autowr_gdc_cfg gdc_cfg; |
| struct autowr_dev_base *dev_base = NULL; |
| |
| memset(&frm_info, 0, sizeof(frm_info)); |
| dev_base = p_file->private_data; |
| |
| switch (cmd) { |
| case AUTOWR_MMAP_FLAG: |
| mutex_lock(&dev_base->pmutex); |
| if (copy_from_user(&frmbuf_info, argp, sizeof(frmbuf_info))) { |
| rtn = -1; |
| mutex_unlock(&dev_base->pmutex); |
| pr_err("Failed to copy\n"); |
| break; |
| } |
| if (dev_base->f_mmap == AUTOWR_MAX_PATH) { |
| dev_base->f_mmap = frmbuf_info.path_type; |
| autowr_get_frmbuf_info(dev_base, &frmbuf_info); |
| } else { |
| pr_err("Error mmap flag\n"); |
| } |
| |
| if (copy_to_user(argp, &frmbuf_info, sizeof(frmbuf_info))) { |
| rtn = -1; |
| pr_err("Failed to copy\n"); |
| } |
| |
| mutex_unlock(&dev_base->pmutex); |
| break; |
| case AUTOWR_GDC_CONFIG: |
| if (copy_from_user(&gdc_cfg, argp, sizeof(gdc_cfg))) { |
| rtn = -1; |
| pr_err("Failed to copy\n"); |
| break; |
| } |
| |
| rtn = autowr_gdc_init(dev_base, &gdc_cfg); |
| if (rtn) { |
| pr_err("Failed to init auto gdc\n"); |
| break; |
| } |
| |
| auto_gdc_cfg_setting(dev_base, &gdc_cfg); |
| break; |
| case AUTOWR_GET_FRMINFO: |
| if (copy_from_user(&frm_info, argp, sizeof(frm_info))) { |
| rtn = -1; |
| pr_err("Failed to copy\n"); |
| break; |
| } |
| |
| rtn = autowr_get_frminfo(dev_base, &frm_info); |
| |
| autowr_gdc_process(dev_base, &frm_info); |
| if (copy_to_user(argp, &frm_info, sizeof(frm_info))) { |
| rtn = -1; |
| pr_err("Failed to copy\n"); |
| } |
| break; |
| case AUTOWR_STOP_CAPTURE: |
| if (copy_from_user(&frm_info, argp, sizeof(frm_info))) { |
| rtn = -1; |
| pr_err("Failed to copy\n"); |
| break; |
| } |
| rtn = autowr_stop_capture(dev_base, &frm_info); |
| autowr_gdc_deinit(dev_base, &frm_info); |
| break; |
| default: |
| rtn = -1; |
| pr_err("Faile to support this cmd\n"); |
| break; |
| } |
| |
| return rtn; |
| } |
| |
| static int autowr_fops_mmap(struct file *p_file, struct vm_area_struct *vma) |
| { |
| s32 rtn = 0; |
| struct autowr_dev_base *dev_base = NULL; |
| |
| dev_base = p_file->private_data; |
| |
| mutex_lock(&dev_base->pmutex); |
| rtn = autowr_mmap_frmbuf_base(dev_base, vma); |
| dev_base->f_mmap = AUTOWR_MAX_PATH; |
| mutex_unlock(&dev_base->pmutex); |
| |
| return rtn; |
| } |
| |
| static const struct file_operations autowr_fops = { |
| .owner = THIS_MODULE, |
| .open = autowr_fops_open, |
| .release = autowr_fops_release, |
| .mmap = autowr_fops_mmap, |
| .unlocked_ioctl = autowr_fops_ioctl, |
| }; |
| |
| static struct miscdevice autowr_misc_dev = { |
| .name = "autowr", |
| .minor = MISC_DYNAMIC_MINOR, |
| .fops = &autowr_fops, |
| }; |
| |
| static void autowr_iounmap(void *base) |
| { |
| struct autowr_dev_base *autowr_base = NULL; |
| |
| if (!base) { |
| pr_err("Error input param\n"); |
| return; |
| } |
| |
| autowr_base = base; |
| |
| if (autowr_base->fr_base) { |
| iounmap(autowr_base->fr_base); |
| autowr_base->fr_base = NULL; |
| } |
| |
| if (autowr_base->ds1_base) { |
| iounmap(autowr_base->ds1_base); |
| autowr_base->ds1_base = NULL; |
| } |
| |
| if (autowr_base->ds2_base) { |
| iounmap(autowr_base->ds2_base); |
| autowr_base->ds2_base = NULL; |
| } |
| } |
| |
| static int autowr_probe(struct platform_device *pdev) |
| { |
| s32 i = 0; |
| s32 rtn = -1; |
| struct resource rs; |
| struct autowr_dev_base *autowr_base = NULL; |
| |
| autowr_base = &autowr_dev; |
| memset(autowr_base, 0, sizeof(*autowr_base)); |
| |
| while (!of_address_to_resource(pdev->dev.of_node, i, &rs)) { |
| if (!strcmp(rs.name, "fr_autowrite")) { |
| autowr_base->fr_base = |
| ioremap_nocache(rs.start, resource_size(&rs)); |
| } else if (!strcmp(rs.name, "ds1_autowrite")) { |
| autowr_base->ds1_base = |
| ioremap_nocache(rs.start, resource_size(&rs)); |
| } else if (!strcmp(rs.name, "ds2_autowrite")) { |
| autowr_base->ds2_base = |
| ioremap_nocache(rs.start, resource_size(&rs)); |
| } else { |
| pr_err("Error index, please check\n"); |
| } |
| |
| i++; |
| } |
| |
| mutex_init(&autowr_base->pmutex); |
| autowr_base->f_mmap = AUTOWR_MAX_PATH; |
| autowr_base->pdev = pdev; |
| |
| rtn = misc_register(&autowr_misc_dev); |
| if (rtn != 0) { |
| autowr_iounmap(autowr_base); |
| pr_err("Failed to register misc dev node"); |
| return rtn; |
| } |
| |
| platform_set_drvdata(pdev, autowr_base); |
| |
| pr_info("Success autowr probe\n"); |
| |
| return rtn; |
| } |
| |
| static int autowr_remove(struct platform_device *pdev) |
| { |
| misc_deregister(&autowr_misc_dev); |
| |
| pr_info("success remove\n"); |
| return 0; |
| } |
| |
| static const struct of_device_id autowrite_dt_match[] = { |
| {.compatible = "amlogic, autowrite"}, |
| {} |
| }; |
| |
| static struct platform_driver p_autowr_drv = { |
| .probe = autowr_probe, |
| .remove = autowr_remove, |
| .driver = { |
| .name = AUTOWR_MODULE_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = autowrite_dt_match, |
| }, |
| }; |
| |
| static int __init autowr_module_init(void) |
| { |
| s32 rtn = -1; |
| |
| rtn = platform_driver_register(&p_autowr_drv); |
| if (rtn != 0) { |
| pr_err("Failed to register autowr driver\n"); |
| } |
| |
| return rtn; |
| } |
| |
| static void __exit autowr_module_exit(void) |
| { |
| platform_driver_unregister(&p_autowr_drv); |
| } |
| |
| module_init(autowr_module_init); |
| module_exit(autowr_module_exit); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Amlogic"); |