123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * Copyright (C) 2021 - 2022 Alibaba Group. 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
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include <linux/errno.h>
- #include <linux/ioctl.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/interrupt.h>
- #include <linux/sched.h>
- #include <linux/semaphore.h>
- #include <linux/spinlock.h>
- #include <asm/io.h>
- #include <linux/cdev.h>
- #include <linux/pci.h>
- #include <linux/uaccess.h>
- #include <linux/ioport.h>
- #include <asm/irq.h>
- #include <linux/version.h>
- #include <linux/vmalloc.h>
- #include <linux/timer.h>
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <linux/of.h>
- #include "isp_venc_shake_driver.h"
- #define IVS_SWREG_AMOUNT 27
- typedef struct _theadivs_dev
- {
- struct cdev cdev;
- dev_t devt;
- struct class *class;
- unsigned long base_addr;
- u32 iosize;
- volatile u8 *hwregs;
- int irq;
- int state;
- } theadivs_dev;
- static int theadivs_major = 0; /* dynamic */
- static int theadivs_minor = 0;
- static theadivs_dev* theadivs_data = NULL;
- static unsigned int device_register_index = 0;
- struct class *theadivs_class;
- static irqreturn_t theadivs_isr(int irq, void *dev_id);
- static void print_registers(void)
- {
- printk("pic_width = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x00)));
- printk("pic_height = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x04)));
- printk("encode_width = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x08)));
- printk("encode_height = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x0c)));
- printk("wid_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x10)));
- printk("wid_uv = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x14)));
- printk("sram_size = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x18)));
- printk("encode_n = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x1c)));
- printk("stride_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x20)));
- printk("stride_uv = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x24)));
- printk("encode_x = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x2c)));
- printk("encode_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x30)));
- printk("int_state = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x38)));
- printk("int_mask = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x40)));
- }
- static int config_ivs(struct file *filp, struct ivs_parameter *params)
- {
- iowrite32(params->pic_width, (void*)(theadivs_data->hwregs + 0x00));
- iowrite32(params->pic_height, (void*)(theadivs_data->hwregs + 0x04));
- iowrite32(params->encode_width, (void*)(theadivs_data->hwregs + 0x08));
- iowrite32(params->encode_height, (void*)(theadivs_data->hwregs + 0x0c));
- iowrite32(params->wid_y, (void*)(theadivs_data->hwregs + 0x10));
- iowrite32(params->wid_uv, (void*)(theadivs_data->hwregs + 0x14));
- iowrite32(params->sram_size, (void*)(theadivs_data->hwregs + 0x18));
- iowrite32(params->encode_n, (void*)(theadivs_data->hwregs + 0x1c));
- iowrite32(params->stride_y, (void*)(theadivs_data->hwregs + 0x20));
- iowrite32(params->stride_uv, (void*)(theadivs_data->hwregs + 0x24));
- iowrite32(1, (void*)(theadivs_data->hwregs + 0x28)); // CLEAR
- iowrite32(params->encode_x, (void*)(theadivs_data->hwregs + 0x2c));
- iowrite32(params->encode_y, (void*)(theadivs_data->hwregs + 0x30));
- iowrite32(0, (void*)(theadivs_data->hwregs + 0x34)); // START
- iowrite32(0, (void*)(theadivs_data->hwregs + 0x3c)); // INT_CLEAN
- iowrite32(params->int_mask, (void*)(theadivs_data->hwregs + 0x40));
- return 0;
- }
- static long theadivs_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
- {
- int err = 0;
- if (_IOC_TYPE(cmd) != THEAD_IOC_MAGIC)
- return -ENOTTY;
- else if (_IOC_NR(cmd) > THEAD_IOC_MAXNR)
- return -ENOTTY;
- if (_IOC_DIR(cmd) & _IOC_READ)
- err = !access_ok((void *) arg, _IOC_SIZE(cmd));
- else if (_IOC_DIR(cmd) & _IOC_WRITE)
- err = !access_ok((void *) arg, _IOC_SIZE(cmd));
- if (err)
- return -EFAULT;
- switch (cmd)
- {
- case THEAD_IOCH_CONFIG_IVS:
- {
- struct ivs_parameter params;
- printk("%s: THEAD_IOCH_CONFIG_IVS\n", __func__);
- err = copy_from_user(¶ms, (struct ivs_parameter*)arg, sizeof(struct ivs_parameter));
- config_ivs(filp, ¶ms);
- print_registers();
- theadivs_data->state = THEADIVS_READY;
- break;
- }
- case THEAD_IOCH_START_IVS:
- {
- printk("%s: THEAD_IOCH_START_IVS\n", __func__);
- iowrite32(1, theadivs_data->hwregs + 0x34); // START
- print_registers();
- theadivs_data->state = THEADIVS_RUNNING;
- break;
- }
- case THEAD_IOCH_RESET_IVS:
- {
- printk("%s: THEAD_IOCH_RESET_IVS\n", __func__);
- iowrite32(1, theadivs_data->hwregs + 0x40); // INT_MASK
- iowrite32(1, theadivs_data->hwregs + 0x28); // CLEAR
- iowrite32(0, theadivs_data->hwregs + 0x40); // INT_MASK
- print_registers();
- theadivs_data->state = THEADIVS_IDLE;
- break;
- }
- case THEAD_IOCH_GET_STATE:
- {
- printk("%s: THEAD_IOCH_GET_STATE: %d\n", __func__, theadivs_data->state);
- err = copy_to_user((int *)arg, &theadivs_data->state, sizeof(int));
- break;
- }
- default:
- {
- printk("%s: undefined command: 0x%x\n", __func__, cmd);
- }
- }
- return 0;
- }
- static int theadivs_open(struct inode *inode, struct file *filp)
- {
- int result = 0;
- //theadivs_dev *dev = theadivs_data;
- //filp->private_data = (void *) dev;
- return result;
- }
- static int theadivs_release(struct inode *inode, struct file *filp)
- {
- //theadivs_dev *dev = (theadivs_dev *) filp->private_data;
- return 0;
- }
- static struct file_operations theadivs_fops = {
- .owner= THIS_MODULE,
- .open = theadivs_open,
- .release = theadivs_release,
- .unlocked_ioctl = theadivs_ioctl,
- .fasync = NULL,
- };
- static const struct of_device_id thead_of_match[] = {
- { .compatible = "thead,light-ivs", },
- { /* sentinel */ },
- };
- static int theadivs_reserve_IO(void)
- {
- if(!request_mem_region
- (theadivs_data->base_addr, theadivs_data->iosize, "shake"))
- {
- printk(KERN_INFO "theadivs: failed to reserve HW regs\n");
- printk(KERN_INFO "theadivs: base_addr = 0x%08lx, iosize = %d\n",
- theadivs_data->base_addr,
- theadivs_data->iosize);
- return -1;
- }
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0))
- theadivs_data->hwregs =
- (volatile u8 *) ioremap_nocache(theadivs_data->base_addr,
- theadivs_data->iosize);
- #else
- theadivs_data->hwregs =
- (volatile u8 *) ioremap(theadivs_data->base_addr,
- theadivs_data->iosize);
- #endif
- if (theadivs_data->hwregs == NULL)
- {
- printk(KERN_INFO "theadivs: failed to ioremap HW regs\n");
- release_mem_region(theadivs_data->base_addr, theadivs_data->iosize);
- return -1;
- }
- printk("theadivs: mapped from 0x%lx to %p with size %d\n",
- theadivs_data->base_addr, theadivs_data->hwregs, theadivs_data->iosize);
- return 0;
- }
- static void theadivs_release_IO(void)
- {
- if(theadivs_data->hwregs)
- {
- iounmap((void *) theadivs_data->hwregs);
- release_mem_region(theadivs_data->base_addr, theadivs_data->iosize);
- theadivs_data->hwregs = NULL;
- }
- }
- int __init theadivs_probe(struct platform_device *pdev)
- {
- int result = -1;
- struct resource *mem;
- printk("enter %s\n",__func__);
- theadivs_data = (theadivs_dev *)vmalloc(sizeof(theadivs_dev));
- if (theadivs_data == NULL)
- return result;
- memset(theadivs_data, 0, sizeof(theadivs_dev));
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if(mem->start)
- theadivs_data->base_addr = mem->start;
- theadivs_data->irq = platform_get_irq(pdev, 0);
- printk("%s:get irq %d\n", __func__, theadivs_data->irq);
- theadivs_data->iosize = IVS_SWREG_AMOUNT * 4;
- #if 1
- if (device_register_index == 0)
- {
- if (theadivs_major == 0)
- {
- result = alloc_chrdev_region(&theadivs_data->devt, 0, 1, "shake");
- if (result != 0)
- {
- printk("%s:alloc_chrdev_region error\n", __func__);
- goto err1;
- }
- theadivs_major = MAJOR(theadivs_data->devt);
- theadivs_minor = MINOR(theadivs_data->devt);
- }
- else
- {
- theadivs_data->devt = MKDEV(theadivs_major, theadivs_minor);
- result = register_chrdev_region(theadivs_data->devt, 1, "shake");
- if (result)
- {
- printk("%s:register_chrdev_region error\n", __func__);
- goto err1;
- }
- }
- theadivs_class = class_create(THIS_MODULE, "shake");
- if (IS_ERR(theadivs_class))
- {
- printk("%s[%d]:class_create error!\n", __func__, __LINE__);
- goto err;
- }
- }
- theadivs_data->devt = MKDEV(theadivs_major, theadivs_minor + pdev->id);
- cdev_init(&theadivs_data->cdev, &theadivs_fops);
- result = cdev_add(&theadivs_data->cdev, theadivs_data->devt, 1);
- if ( result )
- {
- printk("%s[%d]:cdev_add error!\n", __func__, __LINE__);
- goto err;
- }
- theadivs_data->class = theadivs_class;
- device_create(theadivs_data->class, NULL, theadivs_data->devt,
- theadivs_data, "shake");
- device_register_index++;
- #else
- result = register_chrdev(theadivs_major, "shake", &theadivs_fops);
- if (result < 0)
- {
- printk(KERN_INFO "theadivs_driver: unable to get major <%d>\n",
- theadivs_major);
- goto err1;
- }
- else if (result != 0) /* this is for dynamic major */
- {
- theadivs_major = result;
- }
- #endif
- theadivs_reserve_IO();
- /* get the IRQ line */
- if (theadivs_data->irq!= -1)
- {
- result = request_irq(theadivs_data->irq, theadivs_isr,
- IRQF_SHARED,
- "shake", (void *)theadivs_data);
- if (result == -EINVAL)
- {
- printk(KERN_ERR "theadivs_driver: Bad irq number or handler.\n");
- theadivs_release_IO();
- goto err;
- }
- else if (result == -EBUSY)
- {
- printk(KERN_ERR "theadivs_driver: IRQ <%d> busy, change your config.\n",
- theadivs_data->irq);
- theadivs_release_IO();
- goto err;
- }
- }
- else
- {
- printk(KERN_INFO "theadivs_driver: IRQ not in use!\n");
- }
- printk(KERN_INFO "theadivs_driver: module inserted. Major <%d>\n", theadivs_major);
- return 0;
- err:
- //unregister_chrdev(theadivs_major, "shake");
- unregister_chrdev_region(theadivs_data->devt, 1);
- err1:
- if (theadivs_data != NULL)
- vfree(theadivs_data);
- printk(KERN_INFO "theadivs_driver: module not inserted\n");
- return result;
- }
- static int theadivs_remove(struct platform_device *pdev)
- {
- free_irq(theadivs_data->irq, theadivs_data);
- theadivs_release_IO();
- //unregister_chrdev(theadivs_major, "shake");
- device_register_index--;
- cdev_del(&theadivs_data->cdev);
- device_destroy(theadivs_data->class, theadivs_data->devt);
- unregister_chrdev_region(theadivs_data->devt, 1);
- if (device_register_index == 0)
- {
- class_destroy(theadivs_data->class);
- }
- if (theadivs_data != NULL)
- vfree(theadivs_data);
- printk(KERN_INFO "theadivs_driver: module removed\n");
- return 0;
- }
- static struct platform_driver theadivs_driver = {
- .probe = theadivs_probe,
- .remove = theadivs_remove,
- .driver = {
- .name = "shake",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(thead_of_match),
- }
- };
- int __init theadivs_init(void)
- {
- int ret = 0;
- printk("enter %s\n",__func__);
- #if 1
- ret = platform_driver_register(&theadivs_driver);
- if (ret)
- {
- pr_err("register platform driver failed!\n");
- }
- #endif
- return ret;
- }
- void __exit theadivs_cleanup(void)
- {
- printk("enter %s\n",__func__);
- platform_driver_unregister(&theadivs_driver);
- return;
- }
- static irqreturn_t theadivs_isr(int irq, void *dev_id)
- {
- theadivs_dev *dev = (theadivs_dev *) dev_id;
- u32 irq_status = 0;
- printk( "theadivs_isr: received IRQ!\n");
- irq_status = (u32)ioread32((void*)dev->hwregs + 0x38); // INT_STATE
- printk( "INT_STATE of is: 0x%x\n", irq_status);
- theadivs_data->state = THEADIVS_ERROR;
- iowrite32(1, theadivs_data->hwregs + 0x40); // INT_MASK
- iowrite32(1, theadivs_data->hwregs + 0x3c); // INT_CLEAN
- iowrite32(1, theadivs_data->hwregs + 0x28); // CLEAR
- iowrite32(0, theadivs_data->hwregs + 0x40); // INT_MASK
- return IRQ_HANDLED;
- }
- module_init(theadivs_init);
- module_exit(theadivs_cleanup);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("T-HEAD");
- MODULE_DESCRIPTION("T-HEAD ISP-VENC-SHAKE driver");
|